feat(client): record computer tasks for digital employee
This commit is contained in:
@@ -48,6 +48,25 @@ export interface DigitalEmployeeState {
|
||||
pendingSyncCount?: number;
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeChatRequestRecord {
|
||||
user_id?: string;
|
||||
project_id?: string;
|
||||
prompt?: string;
|
||||
session_id?: string;
|
||||
request_id?: string;
|
||||
model_provider?: { provider?: string; model?: string; default_model?: string };
|
||||
}
|
||||
|
||||
export interface DigitalEmployeeChatResultRecord {
|
||||
success: boolean;
|
||||
code?: string;
|
||||
message?: string;
|
||||
project_id?: string;
|
||||
session_id?: string;
|
||||
request_id?: string;
|
||||
engine?: string;
|
||||
}
|
||||
|
||||
function defaultState(): DigitalEmployeeState {
|
||||
return {
|
||||
version: 1,
|
||||
@@ -96,6 +115,121 @@ export function recordDigitalEmployeeSnapshot(
|
||||
return next;
|
||||
}
|
||||
|
||||
export function recordDigitalEmployeeChatReceived(
|
||||
request: DigitalEmployeeChatRequestRecord,
|
||||
): { planId: string; taskId: string; runId: string } {
|
||||
const now = new Date().toISOString();
|
||||
const ids = chatIds(request);
|
||||
const title = chatTitle(request);
|
||||
|
||||
const db = getDb();
|
||||
if (!db) return ids;
|
||||
|
||||
const tx = db.transaction(() => {
|
||||
upsertPlan({
|
||||
id: ids.planId,
|
||||
title: `远程任务:${title}`,
|
||||
objective: request.prompt || request.project_id || "管理端下发任务",
|
||||
status: "running",
|
||||
payload: request,
|
||||
now,
|
||||
});
|
||||
upsertTask({
|
||||
id: ids.taskId,
|
||||
planId: ids.planId,
|
||||
title,
|
||||
status: "running",
|
||||
assignedSkillId: "qimingclaw-computer-control",
|
||||
payload: request,
|
||||
now,
|
||||
});
|
||||
upsertRun({
|
||||
id: ids.runId,
|
||||
planId: ids.planId,
|
||||
taskId: ids.taskId,
|
||||
status: "running",
|
||||
payload: { request },
|
||||
now,
|
||||
});
|
||||
insertEvent(
|
||||
{
|
||||
event_id: `${ids.runId}:received`,
|
||||
kind: "computer_chat_received",
|
||||
occurred_at: now,
|
||||
message: `收到管理端任务:${title}`,
|
||||
plan_id: ids.planId,
|
||||
task_id: ids.taskId,
|
||||
},
|
||||
ids.runId,
|
||||
{ request },
|
||||
);
|
||||
});
|
||||
tx();
|
||||
|
||||
return ids;
|
||||
}
|
||||
|
||||
export function recordDigitalEmployeeChatResult(
|
||||
request: DigitalEmployeeChatRequestRecord,
|
||||
result: DigitalEmployeeChatResultRecord,
|
||||
): void {
|
||||
const now = new Date().toISOString();
|
||||
const ids = chatIds({
|
||||
...request,
|
||||
request_id: request.request_id || result.request_id,
|
||||
session_id: request.session_id || result.session_id,
|
||||
project_id: request.project_id || result.project_id,
|
||||
});
|
||||
const status = result.success ? "completed" : "failed";
|
||||
const title = chatTitle(request);
|
||||
const db = getDb();
|
||||
if (!db) return;
|
||||
|
||||
const tx = db.transaction(() => {
|
||||
upsertPlan({
|
||||
id: ids.planId,
|
||||
title: `远程任务:${title}`,
|
||||
objective: request.prompt || request.project_id || "管理端下发任务",
|
||||
status,
|
||||
payload: { request, result },
|
||||
now,
|
||||
});
|
||||
upsertTask({
|
||||
id: ids.taskId,
|
||||
planId: ids.planId,
|
||||
title,
|
||||
status,
|
||||
assignedSkillId: "qimingclaw-computer-control",
|
||||
payload: { request, result },
|
||||
now,
|
||||
});
|
||||
upsertRun({
|
||||
id: ids.runId,
|
||||
planId: ids.planId,
|
||||
taskId: ids.taskId,
|
||||
status,
|
||||
payload: { request, result },
|
||||
now,
|
||||
});
|
||||
finishRun(ids.runId, status, now);
|
||||
insertEvent(
|
||||
{
|
||||
event_id: `${ids.runId}:${status}`,
|
||||
kind: result.success ? "computer_chat_completed" : "computer_chat_failed",
|
||||
occurred_at: now,
|
||||
message: result.success
|
||||
? `管理端任务完成:session=${result.session_id || request.session_id || "unknown"}`
|
||||
: `管理端任务失败:${result.message || result.code || "unknown error"}`,
|
||||
plan_id: ids.planId,
|
||||
task_id: ids.taskId,
|
||||
},
|
||||
ids.runId,
|
||||
{ request, result },
|
||||
);
|
||||
});
|
||||
tx();
|
||||
}
|
||||
|
||||
function persistSnapshotTables(
|
||||
snapshot: DigitalEmployeeSnapshot,
|
||||
event: DigitalEmployeeStateEvent | null,
|
||||
@@ -315,6 +449,16 @@ function upsertRun(input: {
|
||||
markOutbox("run", input.id, "upsert", input, input.now);
|
||||
}
|
||||
|
||||
function finishRun(runId: string, status: string, now: string): void {
|
||||
const db = getDb();
|
||||
if (!db) return;
|
||||
db.prepare(`
|
||||
UPDATE digital_runs
|
||||
SET status = ?, finished_at = ?, updated_at = ?, sync_status = 'pending'
|
||||
WHERE id = ?
|
||||
`).run(status, now, now, runId);
|
||||
}
|
||||
|
||||
function insertEvent(
|
||||
event: DigitalEmployeeStateEvent,
|
||||
runId: string,
|
||||
@@ -340,6 +484,29 @@ function insertEvent(
|
||||
markOutbox("event", event.event_id, "insert", event, event.occurred_at);
|
||||
}
|
||||
|
||||
function chatIds(request: DigitalEmployeeChatRequestRecord): {
|
||||
planId: string;
|
||||
taskId: string;
|
||||
runId: string;
|
||||
} {
|
||||
const base = safeId(request.request_id || request.session_id || request.project_id || `${Date.now()}`);
|
||||
return {
|
||||
planId: `computer-chat-plan-${base}`,
|
||||
taskId: `computer-chat-task-${base}`,
|
||||
runId: `computer-chat-run-${base}`,
|
||||
};
|
||||
}
|
||||
|
||||
function chatTitle(request: DigitalEmployeeChatRequestRecord): string {
|
||||
const prompt = (request.prompt || "").replace(/\s+/g, " ").trim();
|
||||
if (prompt) return prompt.length > 48 ? `${prompt.slice(0, 48)}...` : prompt;
|
||||
return request.project_id ? `项目 ${request.project_id}` : "管理端下发任务";
|
||||
}
|
||||
|
||||
function safeId(value: string): string {
|
||||
return value.replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
||||
}
|
||||
|
||||
function eventFromSnapshot(
|
||||
snapshot: DigitalEmployeeSnapshot,
|
||||
previous: DigitalEmployeeSnapshot | null,
|
||||
|
||||
Reference in New Issue
Block a user