吸收数字员工PlanStep阻塞处理入口
This commit is contained in:
@@ -2248,6 +2248,41 @@
|
||||
color: #b42318;
|
||||
border-color: rgba(180,35,24,0.22);
|
||||
}
|
||||
.de-blocked-step-control-strip {
|
||||
border-top: 1px solid rgba(180,35,24,0.12);
|
||||
padding-top: 6px;
|
||||
}
|
||||
.de-blocked-step-control {
|
||||
border-color: rgba(180,35,24,0.18);
|
||||
background: rgba(255,248,247,0.92);
|
||||
}
|
||||
.de-blocked-step-control .de-task-agent-control-actions {
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.de-blocked-input-modal { max-width: 520px; }
|
||||
.de-blocked-input-field {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
color: #17344f;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.de-blocked-input-field textarea {
|
||||
min-height: 120px;
|
||||
resize: vertical;
|
||||
border: 1px solid rgba(79,172,254,0.2);
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
color: #12344f;
|
||||
background: rgba(255,255,255,0.82);
|
||||
}
|
||||
.de-blocked-input-field textarea:focus {
|
||||
outline: none;
|
||||
border-color: rgba(79,172,254,0.55);
|
||||
box-shadow: 0 0 0 3px rgba(79,172,254,0.14);
|
||||
}
|
||||
.de-run-detail-list-scroll {
|
||||
overscroll-behavior: contain;
|
||||
scrollbar-gutter: stable;
|
||||
|
||||
@@ -13,6 +13,8 @@ import type {
|
||||
ChannelDetail,
|
||||
BrowserSession,
|
||||
ApprovalSubscriptionPreferences,
|
||||
BusinessViewResponse,
|
||||
PlanStepBusinessRow,
|
||||
PlanStepDispatchPolicy,
|
||||
NotificationPreferences,
|
||||
UserNotification,
|
||||
@@ -609,22 +611,22 @@ export function patchPlanStepDispatchPolicy(
|
||||
});
|
||||
}
|
||||
|
||||
export function getPlanSteps(params: Record<string, string | number | null | undefined> = {}): Promise<any> {
|
||||
export function getPlanSteps(params: Record<string, string | number | null | undefined> = {}): Promise<BusinessViewResponse<PlanStepBusinessRow>> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch(`/api/digital-employee/plan-steps${suffix ? `?${suffix}` : ''}`);
|
||||
return apiFetch<BusinessViewResponse<PlanStepBusinessRow>>(`/api/digital-employee/plan-steps${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function getPlanStepGraph(params: Record<string, string | number | null | undefined> = {}): Promise<any> {
|
||||
export function getPlanStepGraph(params: Record<string, string | number | null | undefined> = {}): Promise<BusinessViewResponse<PlanStepBusinessRow>> {
|
||||
const query = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && `${value}`.trim() !== '') query.set(key, `${value}`);
|
||||
});
|
||||
const suffix = query.toString();
|
||||
return apiFetch(`/api/digital-employee/plan-step-graph${suffix ? `?${suffix}` : ''}`);
|
||||
return apiFetch<BusinessViewResponse<PlanStepBusinessRow>>(`/api/digital-employee/plan-step-graph${suffix ? `?${suffix}` : ''}`);
|
||||
}
|
||||
|
||||
export function markNotificationRead(id: string): Promise<boolean> {
|
||||
|
||||
@@ -2593,11 +2593,13 @@ function planStepRows(snapshot: QimingclawSnapshot | null): BusinessViewRow[] {
|
||||
dependency_status: stringValue(dependencyGraph.status) || stepDependencyStatus(step, blockedDependencies, waitingDependencies),
|
||||
blocked_dependencies: blockedDependencies,
|
||||
blocked_dependency_count: blockedDependencies.length,
|
||||
blocked_dependency_details: planStepDependencyDetails(blockedDependencies, snapshot),
|
||||
waiting_dependencies: waitingDependencies,
|
||||
waiting_dependency_count: waitingDependencies.length,
|
||||
satisfied_dependencies: satisfiedDependencies,
|
||||
preferred_skill_ids: step.preferredSkillIds,
|
||||
task_id: readiness.taskId,
|
||||
latest_run_id: readiness.latestRunId,
|
||||
dispatchable: readiness.dispatchable,
|
||||
skip_reason: readiness.skipReason,
|
||||
last_dispatch_status: stringValue(lastDispatch.status) || null,
|
||||
@@ -2626,8 +2628,11 @@ function planStepGraphRows(snapshot: QimingclawSnapshot | null): BusinessViewRow
|
||||
depends_on: arrayValue(row.depends_on),
|
||||
dependency_status: stringValue(row.dependency_status) || null,
|
||||
blocked_dependencies: arrayValue(row.blocked_dependencies),
|
||||
blocked_dependency_details: arrayValue(row.blocked_dependency_details),
|
||||
waiting_dependencies: arrayValue(row.waiting_dependencies),
|
||||
satisfied_dependencies: arrayValue(row.satisfied_dependencies),
|
||||
task_id: stringValue(row.task_id) || null,
|
||||
latest_run_id: stringValue(row.latest_run_id) || null,
|
||||
dispatchable: row.dispatchable === true,
|
||||
skip_reason: stringValue(row.skip_reason) || null,
|
||||
sync_status: stringValue(row.sync_status) || null,
|
||||
@@ -2661,12 +2666,13 @@ function planStepDispatchReadiness(
|
||||
step: QimingclawPlanStepRecord,
|
||||
snapshot: QimingclawSnapshot | null,
|
||||
payload: Record<string, unknown>,
|
||||
): { taskId: string | null; dispatchable: boolean; skipReason: string | null } {
|
||||
): { taskId: string | null; latestRunId: string | null; dispatchable: boolean; skipReason: string | null } {
|
||||
const tasks = runtimePlanTasks(snapshot, step.planId);
|
||||
const task = tasks.find((candidate) => taskStepId(candidate) === step.id)
|
||||
?? tasks.find((candidate) => !isTerminalRuntimeTaskStatus(candidate.status))
|
||||
?? null;
|
||||
const taskId = task?.id ?? null;
|
||||
const latestRunId = task ? latestRunForTask(runtimeRuns(snapshot), task.id)?.id ?? null : null;
|
||||
let skipReason: string | null = null;
|
||||
if (step.status.toLowerCase() !== 'ready') skipReason = 'not_ready';
|
||||
else if (!task) skipReason = 'missing_task';
|
||||
@@ -2674,7 +2680,39 @@ function planStepDispatchReadiness(
|
||||
else if (runtimeRuns(snapshot).some((run) => run.taskId === task.id && isActiveRuntimeRunStatus(run.status))) skipReason = 'task_already_running';
|
||||
else if (runtimeApprovals(snapshot).some((approval) => isOpenRuntimeApprovalStatus(approval.status) && (approval.planId === step.planId || approval.taskId === task.id || stringValue(asRecord(approval.payload)?.stepId ?? asRecord(approval.payload)?.step_id) === step.id))) skipReason = 'approval_pending';
|
||||
else if (payload.auto_dispatch === false || payload.autoDispatch === false) skipReason = 'auto_dispatch_disabled';
|
||||
return { taskId, dispatchable: !skipReason, skipReason };
|
||||
return { taskId, latestRunId, dispatchable: !skipReason, skipReason };
|
||||
}
|
||||
|
||||
function planStepDependencyDetails(dependencyIds: string[], snapshot: QimingclawSnapshot | null): BusinessViewRow[] {
|
||||
const stepsById = new Map(runtimePlanSteps(snapshot).map((step) => [step.id, step]));
|
||||
return dependencyIds.map((dependencyId) => {
|
||||
const dependency = stepsById.get(dependencyId);
|
||||
if (!dependency) {
|
||||
return {
|
||||
step_id: dependencyId,
|
||||
task_id: null,
|
||||
latest_run_id: null,
|
||||
title: dependencyId,
|
||||
status: 'missing',
|
||||
dependency_status: 'missing',
|
||||
dispatchable: false,
|
||||
skip_reason: 'missing_step',
|
||||
};
|
||||
}
|
||||
const payload = asRecord(dependency.payload) ?? {};
|
||||
const graph = asRecord(payload.dependencyGraph ?? payload.dependency_graph) ?? {};
|
||||
const readiness = planStepDispatchReadiness(dependency, snapshot, payload);
|
||||
return {
|
||||
step_id: dependency.id,
|
||||
task_id: readiness.taskId,
|
||||
latest_run_id: readiness.latestRunId,
|
||||
title: dependency.title,
|
||||
status: dependency.status,
|
||||
dependency_status: stringValue(graph.status) || dependency.status,
|
||||
dispatchable: readiness.dispatchable,
|
||||
skip_reason: readiness.skipReason,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function isTerminalRuntimeTaskStatus(status: string): boolean {
|
||||
|
||||
@@ -6,6 +6,7 @@ import { basePath } from '@/lib/basePath';
|
||||
import {
|
||||
dispatchPlanNow,
|
||||
getDigitalEmployeeWorkday,
|
||||
getPlanStepGraph,
|
||||
getSchedulerJobs,
|
||||
governanceCommand,
|
||||
postDigitalEmployeeWorkdayAction,
|
||||
@@ -26,6 +27,8 @@ import type {
|
||||
DigitalEmployeeWorkEvent,
|
||||
DigitalEmployeeWorkdayProjection,
|
||||
GovernanceCommandKind,
|
||||
PlanStepBusinessRow,
|
||||
PlanStepDependencyDetail,
|
||||
} from '@/types/api';
|
||||
import type { DigitalNavigationTarget } from './navigation';
|
||||
|
||||
@@ -38,6 +41,7 @@ type EmployeeVisualState = 'skill' | 'browser' | 'call' | 'redial' | 'evidence'
|
||||
type EventStatusFilter = 'all' | 'running' | 'success' | 'danger';
|
||||
type TaskListFilter = 'all' | 'selected' | 'pending';
|
||||
type TaskAgentControlCommand = Extract<GovernanceCommandKind, 'retry_task' | 'skip_task' | 'cancel_task'>;
|
||||
type BlockedPlanStepControlCommand = Extract<GovernanceCommandKind, 'retry_task' | 'skip_task' | 'cancel_task' | 'provide_task_input'>;
|
||||
type ApprovalActionKind = 'comment' | 'delegate' | 'mark_timeout' | 'mark_risk' | 'clear_risk';
|
||||
|
||||
interface DigitalPlanItem {
|
||||
@@ -60,6 +64,11 @@ interface DigitalPlanItem {
|
||||
source: 'plan_template';
|
||||
}
|
||||
|
||||
interface BlockedPlanStepInputTarget {
|
||||
step: PlanStepBusinessRow;
|
||||
dependency: PlanStepDependencyDetail | null;
|
||||
}
|
||||
|
||||
interface ManagementSyncStatus {
|
||||
running: boolean;
|
||||
syncing: boolean;
|
||||
@@ -796,6 +805,26 @@ function taskAgentControlLabel(command: TaskAgentControlCommand): string {
|
||||
}
|
||||
}
|
||||
|
||||
function blockedPlanStepControlLabel(command: BlockedPlanStepControlCommand): string {
|
||||
switch (command) {
|
||||
case 'retry_task': return '重试前置';
|
||||
case 'skip_task': return '跳过前置';
|
||||
case 'cancel_task': return '取消此步骤';
|
||||
case 'provide_task_input': return '补充输入';
|
||||
default: return command;
|
||||
}
|
||||
}
|
||||
|
||||
function firstActionableBlockedDependency(step: PlanStepBusinessRow): PlanStepDependencyDetail | null {
|
||||
return (step.blocked_dependency_details ?? []).find((dependency) => Boolean(dependency.task_id)) ?? null;
|
||||
}
|
||||
|
||||
function blockedPlanStepSummary(step: PlanStepBusinessRow): string {
|
||||
const dependency = firstActionableBlockedDependency(step) ?? step.blocked_dependency_details?.[0] ?? null;
|
||||
const dependencyLabel = dependency ? `${dependency.title} ${dependency.status}` : `阻塞依赖 ${step.blocked_dependencies.length} 个`;
|
||||
return `${step.title} ${step.dependency_status || step.status},${dependencyLabel}`;
|
||||
}
|
||||
|
||||
function isDecisionOpen(status?: string): boolean {
|
||||
return ['pending', 'requested'].includes(String(status || '').toLowerCase());
|
||||
}
|
||||
@@ -916,6 +945,10 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
const [profileSettingsOpen, setProfileSettingsOpen] = useState(false);
|
||||
const [reportPreviewOpen, setReportPreviewOpen] = useState(false);
|
||||
const [selectedSyncFailure, setSelectedSyncFailure] = useState<ManagementSyncFailure | null>(null);
|
||||
const [blockedPlanSteps, setBlockedPlanSteps] = useState<PlanStepBusinessRow[]>([]);
|
||||
const [blockedPlanStepError, setBlockedPlanStepError] = useState<string | null>(null);
|
||||
const [blockedInputTarget, setBlockedInputTarget] = useState<BlockedPlanStepInputTarget | null>(null);
|
||||
const [blockedInputDraft, setBlockedInputDraft] = useState('');
|
||||
const [profileForm, setProfileForm] = useState({
|
||||
name: '',
|
||||
company: '',
|
||||
@@ -986,7 +1019,19 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
setManagementSyncStatus(status);
|
||||
});
|
||||
|
||||
await Promise.allSettled([projectionPromise, jobsPromise, syncStatusPromise]);
|
||||
const planStepGraphPromise = getPlanStepGraph({ status: 'blocked', page_size: 6 })
|
||||
.then((response) => {
|
||||
if (!mountedRef.current) return;
|
||||
setBlockedPlanSteps(response.items.filter((step) => step.status === 'blocked'));
|
||||
setBlockedPlanStepError(null);
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (!mountedRef.current) return;
|
||||
setBlockedPlanSteps([]);
|
||||
setBlockedPlanStepError(error instanceof Error ? error.message : '阻塞步骤加载失败');
|
||||
});
|
||||
|
||||
await Promise.allSettled([projectionPromise, jobsPromise, syncStatusPromise, planStepGraphPromise]);
|
||||
if (mountedRef.current) {
|
||||
setProjectionLoading(false);
|
||||
}
|
||||
@@ -1523,6 +1568,71 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
}
|
||||
}, [fetchProjection]);
|
||||
|
||||
const controlBlockedPlanStep = useCallback(async (
|
||||
step: PlanStepBusinessRow,
|
||||
command: BlockedPlanStepControlCommand,
|
||||
options: { input?: string } = {},
|
||||
) => {
|
||||
const dependency = firstActionableBlockedDependency(step);
|
||||
const target = command === 'retry_task' || command === 'skip_task' ? dependency : null;
|
||||
const targetStepId = target?.step_id ?? step.step_id;
|
||||
const targetTaskId = target?.task_id ?? step.task_id ?? null;
|
||||
const targetRunId = target?.latest_run_id ?? step.latest_run_id ?? null;
|
||||
if (!targetTaskId) {
|
||||
setActionError('当前阻塞步骤缺少可治理任务,无法发送处理命令');
|
||||
return;
|
||||
}
|
||||
const actionKey = `blocked-plan-step:${step.step_id}:${command}`;
|
||||
setActionBusy(actionKey);
|
||||
setActionError(null);
|
||||
setActionNotice(null);
|
||||
try {
|
||||
const response = await governanceCommand({
|
||||
command_kind: command,
|
||||
context_refs: {
|
||||
plan_id: step.plan_id,
|
||||
task_id: targetTaskId,
|
||||
run_id: targetRunId,
|
||||
step_id: targetStepId,
|
||||
blocked_step_id: step.step_id,
|
||||
},
|
||||
payload: {
|
||||
source: 'digital-home-blocked-plan-step',
|
||||
plan_id: step.plan_id,
|
||||
task_id: targetTaskId,
|
||||
run_id: targetRunId,
|
||||
step_id: targetStepId,
|
||||
blocked_step_id: step.step_id,
|
||||
blocked_step_title: step.title,
|
||||
blocked_dependency_step_id: target?.step_id ?? null,
|
||||
requested_action: command,
|
||||
input: options.input,
|
||||
},
|
||||
correlation_id: `digital-home-blocked-step-${step.step_id}-${command}-${Date.now()}`,
|
||||
});
|
||||
if (!response.accepted) {
|
||||
throw new Error(response.message || `阻塞步骤处理未被接受:${response.command_id || command}`);
|
||||
}
|
||||
setActionNotice(`阻塞步骤已${blockedPlanStepControlLabel(command)}:${step.title}。`);
|
||||
setBlockedInputTarget(null);
|
||||
setBlockedInputDraft('');
|
||||
await fetchProjection();
|
||||
} catch (error) {
|
||||
if (mountedRef.current) {
|
||||
setActionError(error instanceof Error ? error.message : '阻塞步骤处理失败');
|
||||
}
|
||||
} finally {
|
||||
if (mountedRef.current) setActionBusy(null);
|
||||
}
|
||||
}, [fetchProjection]);
|
||||
|
||||
const submitBlockedPlanStepInput = useCallback(async () => {
|
||||
const target = blockedInputTarget;
|
||||
const input = blockedInputDraft.trim();
|
||||
if (!target || !input) return;
|
||||
await controlBlockedPlanStep(target.step, 'provide_task_input', { input });
|
||||
}, [blockedInputDraft, blockedInputTarget, controlBlockedPlanStep]);
|
||||
|
||||
const restartService = useCallback(async (service: DigitalEmployeeManagedService) => {
|
||||
const actionKey = `managed-service:${service.service_id}:restart`;
|
||||
setActionBusy(actionKey);
|
||||
@@ -2115,6 +2225,72 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(blockedPlanSteps.length > 0 || blockedPlanStepError) && (
|
||||
<div className="de-task-agent-control-strip de-blocked-step-control-strip" aria-label="PlanStep 阻塞处理">
|
||||
{blockedPlanStepError && <p className="de-empty-text">{blockedPlanStepError}</p>}
|
||||
{blockedPlanSteps.slice(0, 3).map((step) => {
|
||||
const dependency = firstActionableBlockedDependency(step);
|
||||
const retryBusy = actionBusy === `blocked-plan-step:${step.step_id}:retry_task`;
|
||||
const skipBusy = actionBusy === `blocked-plan-step:${step.step_id}:skip_task`;
|
||||
const cancelBusy = actionBusy === `blocked-plan-step:${step.step_id}:cancel_task`;
|
||||
const inputBusy = actionBusy === `blocked-plan-step:${step.step_id}:provide_task_input`;
|
||||
const dependencyActionDisabled = Boolean(actionBusy) || !dependency?.task_id;
|
||||
const stepActionDisabled = Boolean(actionBusy) || !step.task_id;
|
||||
return (
|
||||
<div key={step.step_id} className="de-task-agent-control de-blocked-step-control de-live-feed-row--danger">
|
||||
<div className="de-task-agent-control-main">
|
||||
<span>Blocked PlanStep</span>
|
||||
<strong>{step.title}</strong>
|
||||
<small>{blockedPlanStepSummary(step)}</small>
|
||||
</div>
|
||||
<div className="de-task-agent-control-actions">
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-secondary de-btn-sm"
|
||||
disabled={dependencyActionDisabled}
|
||||
title={dependency?.task_id ? '重试首个阻塞前置任务' : '缺少可重试的前置任务'}
|
||||
onClick={() => { void controlBlockedPlanStep(step, 'retry_task'); }}
|
||||
>
|
||||
<RefreshCw size={13} aria-hidden="true" />
|
||||
<span>{retryBusy ? '重试中' : '重试前置'}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-secondary de-btn-sm"
|
||||
disabled={dependencyActionDisabled}
|
||||
title={dependency?.task_id ? '跳过首个阻塞前置任务' : '缺少可跳过的前置任务'}
|
||||
onClick={() => { void controlBlockedPlanStep(step, 'skip_task'); }}
|
||||
>
|
||||
<SkipForward size={13} aria-hidden="true" />
|
||||
<span>{skipBusy ? '跳过中' : '跳过前置'}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-secondary de-btn-sm"
|
||||
disabled={stepActionDisabled}
|
||||
title={step.task_id ? '补充当前阻塞步骤所需输入' : '当前步骤缺少可补充输入的任务'}
|
||||
onClick={() => { setBlockedInputTarget({ step, dependency: null }); setBlockedInputDraft(''); }}
|
||||
>
|
||||
<MessageSquare size={13} aria-hidden="true" />
|
||||
<span>{inputBusy ? '提交中' : '补充输入'}</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-secondary de-btn-sm de-task-agent-cancel-btn"
|
||||
disabled={stepActionDisabled}
|
||||
title={step.task_id ? '取消当前阻塞步骤' : '当前步骤缺少可取消的任务'}
|
||||
onClick={() => { void controlBlockedPlanStep(step, 'cancel_task'); }}
|
||||
>
|
||||
<Square size={12} aria-hidden="true" />
|
||||
<span>{cancelBusy ? '取消中' : '取消此步骤'}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="de-stage-feed-tools">
|
||||
<span>
|
||||
展示 {visibleEvents.length} / 共 {allEvents.length} 条
|
||||
@@ -2292,6 +2468,46 @@ export default function CommandDeck({ onNavigate }: { onNavigate: (target: Digit
|
||||
</div>
|
||||
)}
|
||||
|
||||
{blockedInputTarget && (
|
||||
<div className="de-task-manager-modal-overlay" role="presentation" onClick={() => setBlockedInputTarget(null)}>
|
||||
<div
|
||||
className="de-profile-settings-modal de-blocked-input-modal"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-label="补充阻塞步骤输入"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="de-task-manager-modal-head">
|
||||
<div>
|
||||
<span className="de-drawer-eyebrow">BLOCKED PLANSTEP</span>
|
||||
<h3>补充输入</h3>
|
||||
<p>{blockedInputTarget.step.title}</p>
|
||||
</div>
|
||||
<button type="button" className="de-schedule-close" onClick={() => setBlockedInputTarget(null)}>×</button>
|
||||
</div>
|
||||
<label className="de-blocked-input-field">
|
||||
<span>补充内容</span>
|
||||
<textarea
|
||||
value={blockedInputDraft}
|
||||
onChange={(event) => setBlockedInputDraft(event.target.value)}
|
||||
placeholder="输入用于解除当前阻塞的信息"
|
||||
/>
|
||||
</label>
|
||||
<div className="de-event-modal-actions">
|
||||
<button type="button" className="de-btn-secondary de-btn-sm" onClick={() => setBlockedInputTarget(null)}>取消</button>
|
||||
<button
|
||||
type="button"
|
||||
className="de-btn-primary de-btn-sm"
|
||||
disabled={!blockedInputDraft.trim() || Boolean(actionBusy)}
|
||||
onClick={() => { void submitBlockedPlanStepInput(); }}
|
||||
>
|
||||
提交输入
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{profileSettingsOpen && (
|
||||
<div className="de-task-manager-modal-overlay" role="presentation" onClick={() => setProfileSettingsOpen(false)}>
|
||||
<div
|
||||
|
||||
@@ -202,6 +202,46 @@ export interface PlanStepDispatchPolicy {
|
||||
updated_at: string | null;
|
||||
}
|
||||
|
||||
export interface PlanStepDependencyDetail {
|
||||
step_id: string;
|
||||
task_id: string | null;
|
||||
latest_run_id?: string | null;
|
||||
title: string;
|
||||
status: string;
|
||||
dependency_status: string | null;
|
||||
dispatchable: boolean;
|
||||
skip_reason: string | null;
|
||||
}
|
||||
|
||||
export interface PlanStepBusinessRow {
|
||||
step_id: string;
|
||||
node_id?: string;
|
||||
plan_id: string | null;
|
||||
title: string;
|
||||
status: string;
|
||||
seq?: number | null;
|
||||
task_id?: string | null;
|
||||
latest_run_id?: string | null;
|
||||
depends_on: string[];
|
||||
dependency_status: string | null;
|
||||
blocked_dependencies: string[];
|
||||
waiting_dependencies: string[];
|
||||
satisfied_dependencies: string[];
|
||||
blocked_dependency_details?: PlanStepDependencyDetail[];
|
||||
dispatchable: boolean;
|
||||
skip_reason: string | null;
|
||||
sync_status?: string | null;
|
||||
updated_at?: string | null;
|
||||
}
|
||||
|
||||
export interface BusinessViewResponse<T> {
|
||||
items: T[];
|
||||
page_no: number;
|
||||
page_size: number;
|
||||
total: number;
|
||||
source?: string;
|
||||
}
|
||||
|
||||
export type ChatRouteMode = 'auto' | 'chat' | 'direct' | 'plan';
|
||||
|
||||
export interface WsMessage {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -16,8 +16,8 @@
|
||||
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
||||
}
|
||||
</script>
|
||||
<script type="module" crossorigin src="./assets/index-Dar5kQHh.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-DqdYYbRq.css">
|
||||
<script type="module" crossorigin src="./assets/index-BRZXqRQH.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="./assets/index-4TVkOCsf.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -288,12 +288,16 @@ function checkDigitalPlanStepGraphDispatchPolicy() {
|
||||
const adapterPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "qimingclawAdapter.ts");
|
||||
const apiPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "lib", "api.ts");
|
||||
const typesPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "types", "api.ts");
|
||||
const commandDeckPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "pages", "digital", "CommandDeck.tsx");
|
||||
const cssPath = path.join(packageRoot, "embedded", "sgrobot-digital", "src", "index.css");
|
||||
const stateServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "stateService.ts");
|
||||
const schedulerServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "schedulerService.ts");
|
||||
const syncServicePath = path.join(packageRoot, "src", "main", "services", "digitalEmployee", "syncService.ts");
|
||||
const adapter = fs.readFileSync(adapterPath, "utf8");
|
||||
const api = fs.readFileSync(apiPath, "utf8");
|
||||
const types = fs.readFileSync(typesPath, "utf8");
|
||||
const commandDeck = fs.readFileSync(commandDeckPath, "utf8");
|
||||
const css = fs.readFileSync(cssPath, "utf8");
|
||||
const stateService = fs.readFileSync(stateServicePath, "utf8");
|
||||
const schedulerService = fs.readFileSync(schedulerServicePath, "utf8");
|
||||
const syncService = fs.readFileSync(syncServicePath, "utf8");
|
||||
@@ -304,12 +308,23 @@ function checkDigitalPlanStepGraphDispatchPolicy() {
|
||||
[adapter, "planStepRows", "management plan step rows"],
|
||||
[adapter, "planStepGraphRows", "management plan step graph rows"],
|
||||
[adapter, "filterPlanStepRows", "management plan step filters"],
|
||||
[adapter, "blocked_dependency_details", "plan step blocked dependency details"],
|
||||
[adapter, "planStepDependencyDetails", "plan step blocked dependency detail mapper"],
|
||||
[adapter, "update_plan_step_dispatch_policy", "adapter plan step dispatch policy save action"],
|
||||
[api, "getPlanStepDispatchPolicy", "plan step dispatch policy API getter"],
|
||||
[api, "patchPlanStepDispatchPolicy", "plan step dispatch policy API patcher"],
|
||||
[api, "getPlanSteps", "plan steps API getter"],
|
||||
[api, "getPlanStepGraph", "plan step graph API getter"],
|
||||
[api, "BusinessViewResponse<PlanStepBusinessRow>", "typed plan step business view API"],
|
||||
[types, "PlanStepDispatchPolicy", "plan step dispatch policy type"],
|
||||
[types, "PlanStepDependencyDetail", "plan step blocked dependency detail type"],
|
||||
[types, "PlanStepBusinessRow", "plan step business row type"],
|
||||
[types, "BusinessViewResponse", "business view response type"],
|
||||
[commandDeck, "getPlanStepGraph", "home blocked plan step graph fetch"],
|
||||
[commandDeck, "controlBlockedPlanStep", "home blocked plan step control handler"],
|
||||
[commandDeck, "de-blocked-step-control-strip", "home blocked plan step control strip"],
|
||||
[commandDeck, "provide_task_input", "home blocked plan step input command"],
|
||||
[css, "de-blocked-input-field", "home blocked plan step input styles"],
|
||||
[stateService, "planStepDispatchPolicy", "plan step dispatch policy UI state"],
|
||||
[stateService, "update_plan_step_dispatch_policy", "plan step dispatch policy runtime event"],
|
||||
[schedulerService, "readDigitalEmployeePlanStepDispatchPolicy", "scheduler plan step dispatch policy reader"],
|
||||
|
||||
Reference in New Issue
Block a user