feat(backend,frontend): tâches planifiées projet — tool MCP idea_scheduled_task_* et menu utilisateur #305, fix transcript lié indisponible après reboot #314 (QA verte)

#305: nouvel agrégat durable ScheduledProjectTask dans domain (scheduled_task.rs, ids, ports ScheduledProjectTaskStore), source de vérité <app_data_dir>/scheduled-project-tasks/<projectId>.json, schedules bornés V1 interval/daily/weekly avec fuseau, cycle À approuver/Active/Suspendue avec approbation obligatoire des tâches demandées par agent; use cases application/scheduled_task (list/create/update/delete/run_now/reconcile/fire) réutilisant BackgroundTask pour les runs avec distinction planifié/manuel; store infrastructure dédié + scheduler reconcile au boot sans rattrapage en rafale, cwd confiné au projet; 7 tools MCP idea_scheduled_task_* (list/read lecture seule, create/update/delete/run_now/set_enabled write-actions soumises aux permissions natives); commandes Tauri + routes HTTP aux contrats DTO identiques (camelCase nextRunAtMs/lastRunAtMs/lastTaskId/createdBy/updatedBy); frontend: gateways Tauri/HTTP/mock + types ports/domain, surface projet 'Tâches planifiées' (liste utilisateur+agent unifiée, création/édition avec récapitulatif avant activation, approbation/refus, exécuter maintenant, suspendre/reprendre, suppression), catalogage surface web (webSurfaceCatalog/viewPlacement/ViewPanelBody/WebWorkspace); docs/scheduled-project-tasks.md et sdk docs/services.md (047c38b: pas un service plugin V1, pas d'émulation via cron système).

#314: correction de 'Conversation liée — transcript indisponible' après reboot: restauration du transcript lié côté application (orchestrator service, lifecycle, session_limit, background sink, terminal registry) et côté frontend (adapters agent/http frames/stream/requestResponse, workStateNormalization, CustomAgentChatView avec état et action utile), tests dto_chat/agent_lifecycle/structured_registry_d1/background_completion_sink étendus.

QA verte rapportée par QA sur feature/305-scheduled-project-tasks-mcp, sans bloquant, pour #305 et #314.

Hors périmètre worktree: tickets non liés, attachments/, scripts/; aucun push.
This commit is contained in:
2026-09-11 10:32:01 +02:00
parent 72792bb02b
commit 7007b2cc7e
63 changed files with 3887 additions and 100 deletions

View File

@ -81,6 +81,9 @@ import type {
ResumableAgent,
ResolvedAgentPermissions,
ResolvedAgentSystemPermissions,
ScheduledProjectTask,
ScheduledTaskCommand,
ScheduledTaskSchedule,
ReplyChunk,
SlashCommand,
ExecuteSlashCommandResult,
@ -203,8 +206,14 @@ export interface AgentChatHandle {
readonly cellKind: "chat";
/** Conversation id assigned by launch when the backend minted one. */
readonly assignedConversationId?: string;
/** Why a successful structured open has no replayable transcript. */
readonly transcriptUnavailableReason?: ConversationTranscriptUnavailableReason;
}
export type ConversationTranscriptUnavailableReason =
| "missingTranscript"
| "emptyConversation";
/** Result of re-attaching a custom chat view to a live structured session. */
export interface ReattachAgentChatResult {
/** Effective live structured session id (may replace the requested id after restart). */
@ -213,6 +222,8 @@ export interface ReattachAgentChatResult {
conversationId?: string;
/** Retained chunks already streamed for the session. */
scrollback: ReplyChunk[];
/** Additive reason when `scrollback` is empty. */
transcriptUnavailableReason?: ConversationTranscriptUnavailableReason;
}
/** Stable owner identity used to reconcile a stale transport id after restart. */
@ -1207,6 +1218,35 @@ export interface WorkStateGateway {
retryBackgroundTask(taskId: string): Promise<void>;
}
export interface ScheduledTaskDefinitionInput {
name: string;
description?: string;
command: ScheduledTaskCommand;
schedule: ScheduledTaskSchedule;
timezone: string;
enabled: boolean;
timeoutMs?: number;
}
/** Durable project-scoped schedules; concrete executions remain in WorkState. */
export interface ScheduledTaskGateway {
list(projectId: string): Promise<ScheduledProjectTask[]>;
create(projectId: string, input: ScheduledTaskDefinitionInput): Promise<ScheduledProjectTask>;
update(
projectId: string,
scheduledTaskId: string,
auditRevision: number,
input: ScheduledTaskDefinitionInput,
): Promise<ScheduledProjectTask>;
delete(projectId: string, scheduledTaskId: string): Promise<void>;
runNow(projectId: string, scheduledTaskId: string): Promise<ScheduledProjectTask>;
setEnabled(
projectId: string,
scheduledTaskId: string,
enabled: boolean,
): Promise<ScheduledProjectTask>;
}
/** One paginated page request over a conversation transcript (LS7). */
export interface ConversationPageRequest {
/** Turn id to paginate around; omit to start from a thread end. */
@ -1804,6 +1844,7 @@ export interface Gateways {
device: DeviceGateway;
permission: PermissionGateway;
workState: WorkStateGateway;
scheduledTask: ScheduledTaskGateway;
conversation: ConversationGateway;
ticket: TicketGateway;
window: WindowGateway;