@@ -0,0 +1,678 @@
import type {
CronJob ,
DebugPlan ,
DebugRun ,
DebugTask ,
DigitalEmployeeDuty ,
DigitalEmployeeRunDetail ,
DigitalEmployeeTaskExecutionSummary ,
DigitalEmployeeWorkEvent ,
DigitalEmployeeWorkdayActionRequest ,
DigitalEmployeeWorkdayProjection ,
FlowResponse ,
PlanDispatchResponse ,
SkillSpec ,
} from '@/types/api' ;
declare global {
interface Window {
__QIMINGCLAW_EMBEDDED_DIGITAL__? : boolean ;
}
}
const STATE_KEY = 'qimingclaw:digital-employee-adapter-state' ;
const DEFAULT_REPORT_TIME = '18:00' ;
interface AdapterState {
selectedDutyIdsByDate : Record < string , string [ ] > ;
confirmedDates : Record < string , string > ;
reportScheduleTime : string ;
reportGeneratedAtByDate : Record < string , string > ;
profile : {
name : string ;
company : string ;
position : string ;
currentStatus : string ;
} ;
}
const SKILLS : SkillSpec [ ] = [
{
skill_id : 'qimingclaw-acp-session' ,
name : 'ACP 会话执行' ,
version : '1.0.0' ,
enabled : true ,
description : '通过 qimingclaw 的 ACP 引擎创建会话、执行任务并跟踪结果。' ,
category : '执行' ,
capability_level : 'core' ,
} ,
{
skill_id : 'qimingclaw-mcp-tools' ,
name : 'MCP 工具编排' ,
version : '1.0.0' ,
enabled : true ,
description : '读取已配置 MCP 服务与工具,作为数字员工的工具能力池。' ,
category : '工具' ,
capability_level : 'core' ,
} ,
{
skill_id : 'qimingclaw-computer-control' ,
name : '客户端电脑控制' ,
version : '1.0.0' ,
enabled : true ,
description : '承接 /computer/chat 执行入口,面向远程任务和本地自动化操作。' ,
category : '桌面' ,
capability_level : 'specialized' ,
} ,
{
skill_id : 'qimingclaw-artifact-report' ,
name : '产物与日报整理' ,
version : '1.0.0' ,
enabled : true ,
description : '把执行记录、关键产物和异常汇总成数字员工日报。' ,
category : '汇报' ,
capability_level : 'standard' ,
} ,
] ;
const PLANS : DebugPlan [ ] = [
{
plan_id : 'qimingclaw-client-readiness' ,
title : '客户端运行状态巡检' ,
objective : '确认 qimingclaw 客户端、Agent、MCP 和远程接入能力是否处于可执行状态。' ,
status : 'running' ,
created_at : todayAt ( 9 , 0 ) ,
steps : [
{
step_id : 'agent-status' ,
title : '检查 Agent 引擎状态' ,
depends_on : [ ] ,
preferred_skills : [ 'qimingclaw-acp-session' ] ,
tasks : [ { task_id : 'task-agent-status' , title : '读取 Agent 服务状态' , status : 'running' } ] ,
} ,
{
step_id : 'mcp-status' ,
title : '检查 MCP 工具能力' ,
depends_on : [ 'agent-status' ] ,
preferred_skills : [ 'qimingclaw-mcp-tools' ] ,
tasks : [ { task_id : 'task-mcp-status' , title : '同步 MCP 工具列表' , status : 'pending' } ] ,
} ,
] ,
} ,
{
plan_id : 'qimingclaw-remote-task-ingress' ,
title : '远程任务接入准备' ,
objective : '检查 lanproxy、computer server 和文件服务,为后端下发数字员工任务做准备。' ,
status : 'pending' ,
created_at : todayAt ( 9 , 30 ) ,
steps : [
{
step_id : 'computer-server' ,
title : '确认 /computer/chat 执行入口' ,
depends_on : [ ] ,
preferred_skills : [ 'qimingclaw-computer-control' ] ,
tasks : [ { task_id : 'task-computer-server' , title : '检查电脑控制服务' , status : 'pending' } ] ,
} ,
{
step_id : 'file-artifact' ,
title : '准备产物回传目录' ,
depends_on : [ 'computer-server' ] ,
preferred_skills : [ 'qimingclaw-artifact-report' ] ,
tasks : [ { task_id : 'task-file-artifact' , title : '整理执行产物通道' , status : 'pending' } ] ,
} ,
] ,
} ,
{
plan_id : 'qimingclaw-daily-report' ,
title : '数字员工日报生成' ,
objective : '汇总今日执行记录、服务状态和待介入事项,生成面向业务的日报。' ,
status : 'pending' ,
created_at : todayAt ( 17 , 30 ) ,
steps : [
{
step_id : 'collect-events' ,
title : '汇总运行事件' ,
depends_on : [ ] ,
preferred_skills : [ 'qimingclaw-artifact-report' ] ,
tasks : [ { task_id : 'task-collect-events' , title : '整理今日运行明细' , status : 'pending' } ] ,
} ,
{
step_id : 'write-report' ,
title : '生成日报摘要' ,
depends_on : [ 'collect-events' ] ,
preferred_skills : [ 'qimingclaw-artifact-report' ] ,
tasks : [ { task_id : 'task-write-report' , title : '生成数字员工日报' , status : 'pending' } ] ,
} ,
] ,
} ,
] ;
const PLAN_SCHEDULES : Record < string , { expression : string ; nextRun : string ; scheduleText : string } > = {
'qimingclaw-client-readiness' : {
expression : '0 9 * * *' ,
nextRun : todayAt ( 9 , 0 ) ,
scheduleText : '已定时 09:00 执行' ,
} ,
'qimingclaw-remote-task-ingress' : {
expression : '30 9 * * *' ,
nextRun : todayAt ( 9 , 30 ) ,
scheduleText : '已定时 09:30 执行' ,
} ,
'qimingclaw-daily-report' : {
expression : '0 18 * * *' ,
nextRun : todayAt ( 18 , 0 ) ,
scheduleText : '已定时 18:00 执行' ,
} ,
} ;
export function isQimingclawDigitalApiEnabled ( ) : boolean {
return window . __QIMINGCLAW_EMBEDDED_DIGITAL__ === true ;
}
export async function handleQimingclawDigitalApi < T > (
path : string ,
options : RequestInit = { } ,
) : Promise < T | undefined > {
if ( ! isQimingclawDigitalApiEnabled ( ) ) return undefined ;
const url = new URL ( path , window . location . origin ) ;
const method = ( options . method || 'GET' ) . toUpperCase ( ) ;
const pathname = url . pathname ;
if ( method === 'GET' && pathname === '/api/digital-employee/workday' ) {
return buildWorkday ( url . searchParams . get ( 'date' ) || todayInputDate ( ) ) as T ;
}
if ( method === 'POST' && pathname === '/api/digital-employee/workday/actions' ) {
return handleWorkdayAction ( parseJsonBody < DigitalEmployeeWorkdayActionRequest > ( options . body ) ) as T ;
}
if ( method === 'GET' && pathname === '/api/scheduler/jobs' ) {
return { jobs : buildSchedulerJobs ( ) } as T ;
}
if ( method === 'GET' && pathname === '/api/debug/plans' ) {
return { plans : filterPlans ( url . searchParams . get ( 'q' ) ) } as T ;
}
if ( method === 'GET' && pathname === '/api/debug/tasks' ) {
const planId = url . searchParams . get ( 'plan_id' ) ;
return buildTasksResponse ( planId ) as T ;
}
if ( method === 'GET' && pathname . startsWith ( '/api/debug/plan/' ) && pathname . endsWith ( '/flow' ) ) {
return buildPlanFlow ( decodeURIComponent ( pathname . split ( '/' ) [ 4 ] || '' ) ) as T ;
}
if ( method === 'POST' && pathname . startsWith ( '/api/debug/plan/' ) && pathname . endsWith ( '/dispatch' ) ) {
return dispatchPlan ( decodeURIComponent ( pathname . split ( '/' ) [ 4 ] || '' ) ) as T ;
}
if ( method === 'GET' && pathname . startsWith ( '/api/plan/' ) && pathname . endsWith ( '/messages' ) ) {
return { messages : buildPlanMessages ( decodeURIComponent ( pathname . split ( '/' ) [ 3 ] || '' ) ) } as T ;
}
if ( method === 'GET' && pathname === '/api/skill' ) {
return { skills : SKILLS } as 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 undefined ;
}
function defaultState ( ) : AdapterState {
return {
selectedDutyIdsByDate : { } ,
confirmedDates : { } ,
reportScheduleTime : DEFAULT_REPORT_TIME ,
reportGeneratedAtByDate : { } ,
profile : {
name : '飞天数字员工' ,
company : 'qimingclaw 客户端' ,
position : '客户端任务执行员' ,
currentStatus : 'working' ,
} ,
} ;
}
function readState ( ) : AdapterState {
try {
const raw = window . localStorage . getItem ( STATE_KEY ) ;
return raw ? { . . . defaultState ( ) , . . . JSON . parse ( raw ) as Partial < AdapterState > } : defaultState ( ) ;
} catch {
return defaultState ( ) ;
}
}
function writeState ( state : AdapterState ) : void {
window . localStorage . setItem ( STATE_KEY , JSON . stringify ( state ) ) ;
}
function parseJsonBody < T > ( body : BodyInit | null | undefined ) : T {
if ( typeof body !== 'string' || ! body . trim ( ) ) return { } as T ;
return JSON . parse ( body ) as T ;
}
function todayInputDate ( ) : string {
const date = new Date ( ) ;
return [
date . getFullYear ( ) ,
String ( date . getMonth ( ) + 1 ) . padStart ( 2 , '0' ) ,
String ( date . getDate ( ) ) . padStart ( 2 , '0' ) ,
] . join ( '-' ) ;
}
function todayAt ( hour : number , minute : number ) : string {
const date = new Date ( ) ;
date . setHours ( hour , minute , 0 , 0 ) ;
return date . toISOString ( ) ;
}
function displayDate ( date : string ) : string {
return date . split ( '-' ) . join ( '/' ) ;
}
function selectedSetForDate ( state : AdapterState , date : string ) : Set < string > {
return new Set ( state . selectedDutyIdsByDate [ date ] ? ? [ ] ) ;
}
function planStatusTone ( status : string ) : string {
if ( [ 'completed' , 'succeeded' , 'success' ] . includes ( status ) ) return 'success' ;
if ( [ 'running' , 'active' ] . includes ( status ) ) return 'running' ;
if ( [ 'failed' , 'error' ] . includes ( status ) ) return 'danger' ;
return 'waiting' ;
}
function planStatusLabel ( status : string ) : string {
if ( status === 'running' ) return '执行中' ;
if ( status === 'completed' || status === 'succeeded' ) return '已完成' ;
if ( status === 'failed' ) return '异常' ;
return '待执行' ;
}
function buildSchedulerJobs ( ) : CronJob [ ] {
return PLANS . map ( ( plan ) = > {
const schedule = PLAN_SCHEDULES [ plan . plan_id ] ? ? PLAN_SCHEDULES [ 'qimingclaw-client-readiness' ] ! ;
return {
id : ` schedule- ${ plan . plan_id } ` ,
name : plan.title ,
expression : schedule.expression ,
command : ` digital-employee: ${ plan . plan_id } ` ,
prompt : plan.objective ? ? null ,
job_type : 'plan_template' ,
schedule : { expression : schedule.expression , template_id : plan.plan_id } ,
enabled : true ,
delivery : { mode : 'plan_template' , template_id : plan.plan_id } ,
template_id : plan.plan_id ,
trigger_kind : 'scheduled' ,
step_count : plan.steps?.length ? ? 0 ,
skill_ids : [ . . . new Set ( ( plan . steps ? ? [ ] ) . flatMap ( ( step ) = > step . preferred_skills ? ? [ ] ) ) ] ,
delete_after_run : false ,
created_at : plan.created_at ,
next_run : schedule.nextRun ,
last_run : plan.status === 'running' ? todayAt ( 9 , 5 ) : null ,
last_status : plan.status === 'running' ? 'running' : null ,
last_output : null ,
} ;
} ) ;
}
function buildDuties ( state : AdapterState , date : string ) : DigitalEmployeeDuty [ ] {
const selected = selectedSetForDate ( state , date ) ;
return PLANS . map ( ( plan ) = > {
const schedule = PLAN_SCHEDULES [ plan . plan_id ] ? ? PLAN_SCHEDULES [ 'qimingclaw-client-readiness' ] ! ;
return {
id : plan.plan_id ,
title : plan.title ,
summary : plan.objective ? ? '等待 qimingclaw 执行链路补充任务说明。' ,
implemented : true ,
selectable : true ,
selected : selected.has ( plan . plan_id ) ,
status : plan.status ,
status_label : planStatusLabel ( plan . status ) ,
status_tone : planStatusTone ( plan . status ) ,
schedule_text : schedule.scheduleText ,
result_text : plan.status === 'running' ? '正在同步客户端运行状态' : '等待任务执行' ,
conversation_id : plan.plan_id ,
} ;
} ) ;
}
function buildTasks ( planId? : string | null ) : DebugTask [ ] {
return PLANS
. filter ( ( plan ) = > ! planId || plan . plan_id === planId )
. flatMap ( ( plan ) = > ( plan . steps ? ? [ ] ) . flatMap ( ( step ) = > ( step . tasks ? ? [ ] ) . map ( ( task ) = > ( {
task_id : task.task_id ,
title : task.title ,
status : task.status ,
plan_id : plan.plan_id ,
step_id : step.step_id ,
assigned_skill_id : step.preferred_skills?. [ 0 ] ? ? '' ,
description : ` ${ plan . title } · ${ step . title } ` ,
created_at : plan.created_at ,
updated_at : plan.status === 'running' ? new Date ( ) . toISOString ( ) : plan . created_at ,
started_at : task.status === 'running' ? todayAt ( 9 , 5 ) : null ,
finished_at : task.status === 'completed' ? todayAt ( 9 , 10 ) : null ,
} ) ) ) ) ;
}
function buildRuns ( tasks : DebugTask [ ] ) : DebugRun [ ] {
return tasks
. filter ( ( task ) = > task . status === 'running' || task . status === 'completed' )
. map ( ( task , index ) = > ( {
run_id : ` qimingclaw-run- ${ index + 1 } ` ,
task_id : task.task_id ,
plan_id : task.plan_id ,
skill_id : task.assigned_skill_id ,
attempt_no : 1 ,
lifecycle : task.status === 'running' ? 'Running' : 'Succeeded' ,
created_at : task.started_at ? ? task . created_at ,
completed_at : task.finished_at ,
events : [
{
event_id : ` ${ task . task_id } -event-start ` ,
kind : 'RunStarted' ,
occurred_at : task.started_at ? ? task . created_at ,
message : ` ${ task . title } 已进入 qimingclaw 执行链路 ` ,
} ,
] ,
} ) ) ;
}
function buildTasksResponse ( planId? : string | null ) : { tasks : DebugTask [ ] ; runs : DebugRun [ ] ; total : number ; truncated : boolean } {
const tasks = buildTasks ( planId ) ;
return { tasks , runs : buildRuns ( tasks ) , total : tasks.length , truncated : false } ;
}
function filterPlans ( query? : string | null ) : DebugPlan [ ] {
const normalized = ( query ? ? '' ) . trim ( ) . toLowerCase ( ) ;
if ( ! normalized ) return PLANS ;
return PLANS . filter ( ( plan ) = > (
plan . title . toLowerCase ( ) . includes ( normalized )
|| plan . plan_id . toLowerCase ( ) . includes ( normalized )
|| ( plan . objective ? ? '' ) . toLowerCase ( ) . includes ( normalized )
) ) ;
}
function buildEvents ( duties : DigitalEmployeeDuty [ ] , confirmed : boolean ) : DigitalEmployeeWorkEvent [ ] {
if ( ! confirmed ) {
return [ {
id : 'event-await-confirmation' ,
time : '待确认' ,
plan_title : '任务设置' ,
task_title : '等待确认今日任务' ,
step_title : '选择职责' ,
detail : '请先确认今天要执行的任务,确认后数字员工会开始执行。' ,
status : 'pending' ,
status_label : '待确认' ,
status_tone : 'waiting' ,
updated_at : new Date ( ) . toISOString ( ) ,
conversation_id : null ,
} ] ;
}
const selectedDuties = duties . filter ( ( duty ) = > duty . selected ) ;
return selectedDuties . map ( ( duty , index ) = > ( {
id : ` event- ${ duty . id } ` ,
time : index === 0 ? '09:05' : '待执行' ,
plan_title : duty.title ,
task_title : duty.title ,
step_title : index === 0 ? '接入 qimingclaw 执行链路' : '等待调度' ,
detail : index === 0
? '已接入 qimingclaw 客户端服务状态与执行入口,等待后续绑定真实 Run 事件。'
: '任务已确认,将在后续阶段接入 ACP/MCP 真实执行事件。' ,
status : index === 0 ? 'running' : 'pending' ,
status_label : index === 0 ? '执行中' : '待执行' ,
status_tone : index === 0 ? 'running' : 'waiting' ,
updated_at : new Date ( ) . toISOString ( ) ,
conversation_id : duty.conversation_id ,
} ) ) ;
}
function buildExecutionSummaries ( duties : DigitalEmployeeDuty [ ] , events : DigitalEmployeeWorkEvent [ ] ) : DigitalEmployeeTaskExecutionSummary [ ] {
return duties . filter ( ( duty ) = > duty . selected ) . map ( ( duty ) = > {
const related = events . filter ( ( event ) = > event . conversation_id === duty . conversation_id ) ;
const running = related . filter ( ( event ) = > event . status === 'running' ) . length ;
const success = related . filter ( ( event ) = > event . status === 'completed' ) . length ;
return {
id : duty.id ,
plan_id : duty.id ,
title : duty.title ,
description : duty.summary ,
selected : duty.selected ,
total_count : Math.max ( related . length , 1 ) ,
success_count : success ,
failure_count : 0 ,
running_count : running ,
waiting_count : running > 0 ? 0 : 1 ,
latest_status_label : running > 0 ? '执行中' : '待执行' ,
latest_status_tone : running > 0 ? 'running' : 'waiting' ,
business_summary : duty.result_text ,
ai_summary : '当前由 qimingclaw adapter 生成,后续接入真实执行日志。' ,
} ;
} ) ;
}
function buildRunDetails ( events : DigitalEmployeeWorkEvent [ ] ) : DigitalEmployeeRunDetail [ ] {
return events . map ( ( event ) = > ( {
id : ` run-detail- ${ event . id } ` ,
time : event.time ,
task_name : event.task_title ,
step_name : event.step_title ,
operation_name : event.plan_title ,
status_label : event.status_label ,
status_tone : event.status_tone ,
business_detail : event.detail ,
raw_detail : event.detail ,
updated_at : event.updated_at ,
conversation_id : event.conversation_id ,
} ) ) ;
}
function buildWorkday ( date : string ) : DigitalEmployeeWorkdayProjection {
const state = readState ( ) ;
const confirmedAt = state . confirmedDates [ date ] ? ? null ;
const confirmed = Boolean ( confirmedAt ) ;
const duties = buildDuties ( state , date ) ;
const events = buildEvents ( duties , confirmed ) ;
const executionSummaries = buildExecutionSummaries ( duties , events ) ;
const generatedAt = state . reportGeneratedAtByDate [ date ] ? ? null ;
const selectedCount = duties . filter ( ( duty ) = > duty . selected ) . length ;
const runningCount = events . filter ( ( event ) = > event . status === 'running' ) . length ;
return {
source : 'live' ,
date ,
employee : {
name : state.profile.name ,
title : '数字员工' ,
major : 'qimingclaw 客户端任务执行' ,
company : state.profile.company ,
position : state.profile.position ,
current_status : state.profile.currentStatus ,
current_status_label : state.profile.currentStatus === 'resting' ? '休息中' : '工作中' ,
motto : '承接客户端任务,调度 ACP 与 MCP 能力完成执行。' ,
} ,
workday : {
confirmed ,
confirmed_at : confirmedAt ,
current_status : state.profile.currentStatus ,
current_status_label : state.profile.currentStatus === 'resting' ? '休息中' : '工作中' ,
status_label : confirmed ? '运行中' : '待确认' ,
status_tone : confirmed ? 'running' : 'waiting' ,
summary : confirmed
? ` 当前日期 ${ displayDate ( date ) } 已确认 ${ selectedCount } 项任务。 `
: ` 当前日期 ${ displayDate ( date ) } 待确认今日任务。 ` ,
bubble_text : confirmed
? '我已接入 qimingclaw 客户端执行链路,正在等待真实 Run/Event 数据接入。'
: '请先确认今天要执行的任务,我会把它们映射到 qimingclaw 执行链路。' ,
} ,
overview : {
title : '今日执行' ,
status_label : confirmed ? '真实数据' : '待确认' ,
status_tone : confirmed ? 'success' : 'waiting' ,
metrics : [
{ label : '已选任务' , value : String ( selectedCount ) } ,
{ label : '运行记录' , value : String ( events . length ) } ,
{ label : '执行中' , value : String ( runningCount ) } ,
{ label : '异常' , value : '0' } ,
] ,
} ,
duties ,
events ,
execution_summaries : executionSummaries ,
run_details : buildRunDetails ( events ) ,
decisions : [ ] ,
delivery : {
headline : confirmed ? 'qimingclaw 执行链路已接入' : '等待确认任务设置' ,
summary : '当前阶段已将数字员工页面切换到 qimingclaw adapter, 下一阶段接入真实 ACP/MCP Run 事件。' ,
lines : [
'已吸收 sgRobot 数字员工 UI 和核心展示模型。' ,
'已建立 qimingclaw workday/plans/tasks/skills API adapter。' ,
'后续将把客户端服务状态、会话和执行事件写入真实状态模型。' ,
] ,
artifacts : [
{ name : '数字员工运行摘要' , status : generatedAt ? 'ready' : 'pending' } ,
] ,
} ,
daily_report : {
date ,
title : ` ${ displayDate ( date ) } 飞天数字员工日报 ` ,
report_schedule_time : state.reportScheduleTime ,
status : generatedAt ? 'ready' : 'not_generated' ,
generated_at : generatedAt ,
generated_by : generatedAt ? 'ai' : 'fallback' ,
model : 'qimingclaw-adapter' ,
source : 'qimingclaw' ,
summary : generatedAt
? '今日已完成数字员工页面到 qimingclaw adapter 的接入,可继续接入真实执行事件。'
: '日报将在生成后汇总任务选择、运行明细和后续接入建议。' ,
totals : {
task_count : selectedCount ,
execution_count : events.length ,
success_count : 0 ,
failure_count : 0 ,
running_count : runningCount ,
} ,
task_sections : executionSummaries ,
detail_lines : events.map ( ( event ) = > ` ${ event . time } ${ event . task_title } : ${ event . detail } ` ) ,
pdf_available : false ,
pdf_url : null ,
} ,
} ;
}
function handleWorkdayAction ( body : DigitalEmployeeWorkdayActionRequest ) : DigitalEmployeeWorkdayProjection {
const state = readState ( ) ;
const date = body . date || todayInputDate ( ) ;
if ( body . action === 'confirm_workday' ) {
state . selectedDutyIdsByDate [ date ] = body . duty_ids ? ? [ ] ;
state . confirmedDates [ date ] = new Date ( ) . toISOString ( ) ;
}
if ( body . action === 'update_profile' ) {
state . profile = {
name : body.name || state . profile . name ,
company : body.company || state . profile . company ,
position : body.position || state . profile . position ,
currentStatus : body.current_status || state . profile . currentStatus ,
} ;
}
if ( body . action === 'update_report_schedule' ) {
state . reportScheduleTime = body . report_schedule_time || state . reportScheduleTime ;
}
if ( body . action === 'generate_daily_report' ) {
if ( body . duty_ids ) state . selectedDutyIdsByDate [ date ] = body . duty_ids ;
if ( body . report_schedule_time ) state . reportScheduleTime = body . report_schedule_time ;
state . reportGeneratedAtByDate [ date ] = new Date ( ) . toISOString ( ) ;
}
writeState ( state ) ;
return buildWorkday ( date ) ;
}
function buildPlanFlow ( planId : string ) : FlowResponse {
const plan = PLANS . find ( ( candidate ) = > candidate . plan_id === planId ) ? ? PLANS [ 0 ] ! ;
const timestamp = new Date ( ) . toISOString ( ) ;
return {
plan_id : plan.plan_id ,
plan_title : plan.title ,
plan_status : plan.status ,
plan_objective : plan.objective ,
lanes : [
{ key : 'intent' , label : '目标' , color : '#38bdf8' } ,
{ key : 'qimingclaw' , label : 'qimingclaw' , color : '#22c55e' } ,
{ key : 'execution' , label : '执行' , color : '#f59e0b' } ,
] ,
steps : [
{
seq : 1 ,
lane : 'intent' ,
label : plan.title ,
sub_label : plan.objective ,
timestamp : plan.created_at ,
color : '#38bdf8' ,
source_type : 'plan' ,
} ,
{
seq : 2 ,
lane : 'qimingclaw' ,
label : '接入客户端能力' ,
sub_label : 'ACP / MCP / computer server adapter' ,
timestamp ,
color : '#22c55e' ,
source_type : 'adapter' ,
} ,
{
seq : 3 ,
lane : 'execution' ,
label : plan.status === 'running' ? '等待真实 Run 事件' : '等待调度' ,
sub_label : '后续阶段接入 qimingclaw 本地状态模型' ,
timestamp ,
color : '#f59e0b' ,
source_type : 'run' ,
} ,
] ,
events : [
{
event_id : ` ${ plan . plan_id } -adapter-event ` ,
kind : 'qimingclaw_adapter' ,
occurred_at : timestamp ,
message : '数字员工页面已通过 qimingclaw adapter 获取数据。' ,
} ,
] ,
} ;
}
function dispatchPlan ( planId : string ) : PlanDispatchResponse {
const state = readState ( ) ;
const date = todayInputDate ( ) ;
const selected = new Set ( state . selectedDutyIdsByDate [ date ] ? ? [ ] ) ;
selected . add ( planId ) ;
state . selectedDutyIdsByDate [ date ] = [ . . . selected ] ;
state . confirmedDates [ date ] = state . confirmedDates [ date ] ? ? new Date ( ) . toISOString ( ) ;
writeState ( state ) ;
return {
ok : true ,
plan_id : planId ,
source_plan_id : planId ,
dispatched_tasks : buildTasks ( planId ) . map ( ( task ) = > ( {
task_id : task.task_id ,
title : task.title ,
status : task.status === 'pending' ? 'running' : task . status ,
} ) ) ,
} ;
}
function buildPlanMessages ( planId : string ) : Array < { role : string ; content : string ; created_at? : string | null } > {
const plan = PLANS . find ( ( candidate ) = > candidate . plan_id === planId ) ? ? PLANS [ 0 ] ! ;
return [
{
role : 'user' ,
content : ` 执行数字员工任务: ${ plan . title } ` ,
created_at : plan.created_at ,
} ,
{
role : 'assistant' ,
content : ` 已接入 qimingclaw adapter。目标: ${ plan . objective ? ? plan . title } ` ,
created_at : new Date ( ) . toISOString ( ) ,
} ,
] ;
}