feat(workbench): refine dynamic task collaboration

This commit is contained in:
2026-09-24 16:36:12 +08:00
parent b64d2b78bb
commit 87cc7785da
25 changed files with 1216 additions and 470 deletions
@@ -10,7 +10,8 @@ const taskSourceSchema = z.object({
kind: z.enum(["schedule", "external_system", "operator", "agent"]),
external_id: z.string().min(1).nullable(),
external_revision: z.number().int().positive().nullable(),
event_kind: z.string().min(1).nullable()
event_kind: z.string().min(1).nullable(),
deleted_at: workbenchDateTimeSchema.nullish()
}).strict();
const taskTriggerSchema = z.object({
kind: z.enum(["schedule", "external_sync", "event", "manual"]),
@@ -78,14 +79,19 @@ const planNodeSchema = z.object({
status: z.enum(["pending", "waiting", "running", "completed", "failed", "skipped", "cancelled"]),
detail: z.string().min(1),
evidence_refs: z.array(z.string().min(1)),
action_refs: z.array(z.string().uuid())
action_refs: z.array(z.string().uuid()),
kind: z.enum(["activity", "decision", "parallel_split", "parallel_join", "gate"]).nullish(),
selected_edge_id: z.string().min(1).nullish(),
gate_ref: z.string().uuid().nullish(),
revisits_node_id: z.string().min(1).nullish()
}).strict();
const planInputSchema = z.object({
schema: z.literal("execution-plan@1"),
schema: z.enum(["execution-plan@1", "execution-plan@2"]),
title: z.string().min(1),
reason: z.string().min(1),
nodes: z.array(planNodeSchema).min(1).max(100),
edges: z.array(z.object({
id: z.string().min(1).nullish(),
from: z.string().min(1),
to: z.string().min(1),
condition: z.string().min(1).nullable()
@@ -139,6 +145,8 @@ const actionSchema = z.object({
execution_request_id: z.string().uuid().nullable(),
proposed_by: z.string().min(1),
proposed_at: workbenchDateTimeSchema,
edited_by: z.string().min(1).nullish(),
edited_at: workbenchDateTimeSchema.nullish(),
decided_by: z.string().min(1).nullable(),
decided_at: workbenchDateTimeSchema.nullable(),
decision_comment: z.string().min(1).nullable(),
@@ -147,6 +155,21 @@ const actionSchema = z.object({
receipt: actionReceiptSchema.nullable(),
execution_detail: z.string().min(1).nullable()
}).strict();
const accessRequestSchema = z.object({
access_request_id: z.string().uuid(),
task_id: z.string().uuid(),
node_id: z.string().min(1),
scopes: z.array(z.string().min(1)),
reason: z.string().min(1),
status: z.enum(["pending", "approved", "rejected"]),
approved_scopes: z.array(z.string().min(1)),
version: z.number().int().positive(),
requested_by: z.string().min(1),
requested_at: workbenchDateTimeSchema,
decided_by: z.string().min(1).nullable(),
decided_at: workbenchDateTimeSchema.nullable(),
comment: z.string().min(1).nullable()
}).strict();
const taskPermissionsSchema = z.object({
can_publish_artifacts: z.boolean(),
can_propose_actions: z.boolean(),
@@ -154,7 +177,8 @@ const taskPermissionsSchema = z.object({
can_decide_work_orders: z.boolean(),
can_execute_work_orders: z.boolean(),
can_decide_commands: z.boolean(),
can_execute_commands: z.boolean()
can_execute_commands: z.boolean(),
can_decide_access: z.boolean().default(false)
}).strict();
const externalWorkOrderActivitySchema = z.object({
sequence: z.number().int().positive(),
@@ -192,6 +216,7 @@ const operationalTaskSchema = taskSummarySchema.extend({
latest_report: reportRevisionSchema.nullable(),
evidence: z.array(evidenceSchema),
actions: z.array(actionSchema),
access_requests: z.array(accessRequestSchema).default([]),
permissions: taskPermissionsSchema,
work_order_snapshot: externalWorkOrderSnapshotSchema.nullable().default(null)
}).strict();
@@ -212,6 +237,11 @@ const notificationSchema = z.object({
]),
title: z.string().min(1),
description: z.string().min(1),
lifecycle_status: z.enum(["active", "resolved"]).default("active"),
subject_type: z.enum(["task", "run", "access_request", "action"]).default("task"),
subject_id: z.string().uuid().nullable().default(null),
resolved_at: workbenchDateTimeSchema.nullable().default(null),
resolution_reason: z.string().min(1).nullable().default(null),
status: z.enum(["unread", "read"]),
version: z.number().int().positive(),
required_permission: z.string().min(1).nullable(),
@@ -252,6 +282,7 @@ export type OperationalTaskSummary = z.infer<typeof taskSummarySchema>;
export type OperationalTask = z.infer<typeof operationalTaskSchema>;
export type OperationalTaskPlan = z.infer<typeof planInputSchema>;
export type OperationalTaskAction = z.infer<typeof actionSchema>;
export type OperationalTaskAccessRequest = z.infer<typeof accessRequestSchema>;
export type OperationalTaskEvidence = z.infer<typeof evidenceSchema>;
export type OperationalTaskNotification = z.infer<typeof notificationSchema>;
export type OperationalTaskSchedule = z.infer<typeof scheduleSchema>;
@@ -336,6 +367,34 @@ export async function executeOperationalTaskAction(taskId: string, actionId: str
));
}
export async function updateOperationalWorkOrderDraft(taskId: string, actionId: string, input: {
request_id: string; expected_version: number; payload: Record<string, unknown>;
}, getAccessToken?: AccessTokenProvider) {
return actionSchema.parse(await operationalTaskRequest(
`/operational-tasks/${encodeURIComponent(taskId)}/actions/${encodeURIComponent(actionId)}/draft`,
getAccessToken, { method: "PATCH", body: JSON.stringify(input) }
));
}
export async function confirmOperationalWorkOrderDispatch(taskId: string, actionId: string, input: {
request_id: string; expected_version: number;
}, getAccessToken?: AccessTokenProvider) {
return actionSchema.parse(await operationalTaskRequest(
`/operational-tasks/${encodeURIComponent(taskId)}/actions/${encodeURIComponent(actionId)}/confirm-dispatch`,
getAccessToken, { method: "POST", body: JSON.stringify(input) }
));
}
export async function decideOperationalTaskAccess(taskId: string, accessRequestId: string, input: {
request_id: string; expected_version: number; decision: "approved" | "rejected";
approved_scopes: string[]; comment?: string;
}, getAccessToken?: AccessTokenProvider) {
return accessRequestSchema.parse(await operationalTaskRequest(
`/operational-tasks/${encodeURIComponent(taskId)}/access-requests/${encodeURIComponent(accessRequestId)}/decision`,
getAccessToken, { method: "POST", body: JSON.stringify(input) }
));
}
export async function fetchOperationalTaskNotifications(getAccessToken?: AccessTokenProvider, signal?: AbortSignal) {
return notificationPageSchema.parse(await operationalTaskRequest(
"/operational-task-notifications?unread_only=true&limit=100&offset=0",