feat(tickets): ajoute les pièces jointes sur tickets (#108)

Stockage flat côté ticket + métadonnées d'attachments, lecture exposée côté
backend/MCP, et UI minimale de liste/ajout dans TicketDetail. Traverse le
domaine (Issue, ports), l'application (usecases + assistant de ticket), les
adaptateurs infra/MCP (issues store, orchestrateur), les DTO backend/web-
server/app-tauri, et le frontend (domain/ports/adapters/hooks/UI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-29 11:08:45 +02:00
parent 2692b9cc03
commit 8158057b1d
34 changed files with 1722 additions and 102 deletions

View File

@ -34,20 +34,21 @@ use tokio::task::JoinHandle;
use uuid::Uuid;
use application::{
AppError, AssignIssueAgentInput, AssignSkillToAgentInput, AssignTicketToSprintInput,
AttachLiveAgentInput, AuthenticateSessionInput, ChangeAgentProfileInput, CloseTerminalInput,
CreateAgentInput, CreateMemoryInput, CreateSkillInput, CreateSprintInput, DeleteAgentInput,
DeleteEmbedderProfileInput, DeleteIssueInput, DeleteMemoryInput, DeleteSkillInput,
DeleteSprintInput, DeleteTemplateInput, DetectAgentDriftInput, GetMemoryInput,
GetProjectSystemPermissionsInput, GetProjectWorkStateInput, GitBranchesInput, GitCheckoutInput,
GitCommitInput, GitGraphInput, GitInitInput, GitLogInput, GitStagePathInput, GitStatusInput,
InspectConversationInput, LaunchAgentInput, LinkIssuesInput, ListAgentsInput, ListDevicesInput,
ListIssuesInput, ListMemoriesInput, ListResumableAgentsInput, ListSkillsInput,
ListSprintsInput, LiveSessions, McpRuntime, OpenProjectInput, PairAttemptDecision,
AddIssueAttachmentInput, AppError, AssignIssueAgentInput, AssignSkillToAgentInput,
AssignTicketToSprintInput, AttachLiveAgentInput, AuthenticateSessionInput,
ChangeAgentProfileInput, CloseTerminalInput, CreateAgentInput, CreateMemoryInput,
CreateSkillInput, CreateSprintInput, DeleteAgentInput, DeleteEmbedderProfileInput,
DeleteIssueInput, DeleteMemoryInput, DeleteSkillInput, DeleteSprintInput, DeleteTemplateInput,
DetectAgentDriftInput, GetMemoryInput, GetProjectSystemPermissionsInput,
GetProjectWorkStateInput, GitBranchesInput, GitCheckoutInput, GitCommitInput, GitGraphInput,
GitInitInput, GitLogInput, GitStagePathInput, GitStatusInput, InspectConversationInput,
LaunchAgentInput, LinkIssuesInput, ListAgentsInput, ListDevicesInput, ListIssuesInput,
ListMemoriesInput, ListResumableAgentsInput, ListSkillsInput, ListSprintsInput, LiveSessions,
MarkIssueAttachmentSummarizedInput, McpRuntime, OpenProjectInput, PairAttemptDecision,
PairDeviceInput, RateLimitKey, ReadAgentContextInput, ReadConversationPageInput,
ReadIssueCarnetInput, ReadIssueInput, ReadMcpToolPermissionsInput, ReadMemoryIndexInput,
ReadProjectContextInput, RecallMemoryInput, RenameDeviceInput, RenameSprintInput,
ReorderSprintsInput, ResizeTerminalInput, ResolveAgentPermissionsInput,
ReadIssueAttachmentInput, ReadIssueCarnetInput, ReadIssueInput, ReadMcpToolPermissionsInput,
ReadMemoryIndexInput, ReadProjectContextInput, RecallMemoryInput, RenameDeviceInput,
RenameSprintInput, ReorderSprintsInput, ResizeTerminalInput, ResolveAgentPermissionsInput,
ResolveAgentSystemPermissionsInput, ResolveMemoryLinksInput, RevokeDeviceInput,
RotateConversationLogInput, StopLiveAgentInput, SyncAgentWithTemplateInput, TouchDeviceInput,
UnassignSkillFromAgentInput, UnassignTicketFromSprintInput, UnlinkIssuesInput,
@ -64,33 +65,36 @@ use domain::{
};
use backend::dto::{
create_input, paginate, parse_agent_id, parse_agent_id_dto, parse_link_kind_dto, parse_node_id,
parse_profile_id, parse_project_id, parse_ref_dto, parse_session_id, parse_skill_id,
parse_sprint_id_dto, parse_sprint_status_dto, parse_task_id, parse_template_id,
sort_ticket_rows, sprint_version_dto, update_input, version_dto, AgentDriftListDto, AgentDto,
AgentListDto, AssignSkillRequestDto, AttachLiveAgentRequestDto, AttachLiveAgentResponseDto,
BackgroundTaskDto, ChangeAgentProfileDto, ChangeAgentProfileRequestDto,
CloneOpenCodeProfileFromSeedRequestDto, ConfigureProfilesRequestDto, ConversationDetailsDto,
CreateAgentFromTemplateRequestDto, CreateAgentRequestDto, CreateMemoryRequestDto,
CreateSkillRequestDto, CreateTemplateRequestDto, DetectProfilesRequestDto,
DetectProfilesResponseDto, EffectivePermissionsDto, EmbedderEnginesDto, EmbedderProfileDto,
EmbedderProfileListDto, ErrorDto, FirstRunStateDto, GitBranchesDto, GitCheckoutRequestDto,
GitCommitDto, GitCommitListDto, GitCommitRequestDto, GitStageRequestDto, GitStatusListDto,
GraphCommitListDto, HealthRequestDto, HealthResponseDto, InspectConversationRequestDto,
LaunchAgentRequestDto, LiveAgentListDto, MemoryDto, MemoryIndexDto, MemoryLinksDto,
MemoryListDto, OpenCodeProviderListDto, OpenTerminalRequestDto, ProfileDto, ProfileListDto,
ProfileModelCatalogDto, ProjectDto, ProjectListDto, ProjectMcpToolPermissionsDto,
ProjectPermissionsDto, ProjectSystemPermissionsDto, ProjectWorkStateDto,
ReadAgentContextResponseDto, ReadConversationPageRequestDto, RecallMemoryRequestDto,
ResolveAgentPermissionsRequestDto, ResolveAgentSystemPermissionsRequestDto,
ResolvedAgentSystemPermissionsDto, ResumableAgentListDto, SaveEmbedderProfileRequestDto,
SaveOpenCodeProviderProfileRequestDto, SaveProfileRequestDto, SkillDto, SkillListDto,
SprintCreateRequestDto, SprintDeleteRequestDto, SprintDto, SprintListDto, SprintListRequestDto,
SprintRenameRequestDto, SprintReorderRequestDto, StopLiveAgentRequestDto,
StopLiveAgentResponseDto, SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto,
TemplateListDto, TerminalSessionDto, TicketAssignRequestDto, TicketCarnetDto,
TicketCreateRequestDto, TicketDeleteRequestDto, TicketDto, TicketLinkCommandRequestDto,
TicketListPageInput, TicketListRequestDto, TicketReadRequestDto, TicketSprintAssignRequestDto,
create_input, paginate, parse_agent_id, parse_agent_id_dto, parse_attachment_id_dto,
parse_link_kind_dto, parse_node_id, parse_profile_id, parse_project_id, parse_ref_dto,
parse_session_id, parse_skill_id, parse_sprint_id_dto, parse_sprint_status_dto, parse_task_id,
parse_template_id, sort_ticket_rows, sprint_version_dto, update_input, version_dto,
AgentDriftListDto, AgentDto, AgentListDto, AssignSkillRequestDto, AttachLiveAgentRequestDto,
AttachLiveAgentResponseDto, BackgroundTaskDto, ChangeAgentProfileDto,
ChangeAgentProfileRequestDto, CloneOpenCodeProfileFromSeedRequestDto,
ConfigureProfilesRequestDto, ConversationDetailsDto, CreateAgentFromTemplateRequestDto,
CreateAgentRequestDto, CreateMemoryRequestDto, CreateSkillRequestDto, CreateTemplateRequestDto,
DetectProfilesRequestDto, DetectProfilesResponseDto, EffectivePermissionsDto,
EmbedderEnginesDto, EmbedderProfileDto, EmbedderProfileListDto, ErrorDto, FirstRunStateDto,
GitBranchesDto, GitCheckoutRequestDto, GitCommitDto, GitCommitListDto, GitCommitRequestDto,
GitStageRequestDto, GitStatusListDto, GraphCommitListDto, HealthRequestDto, HealthResponseDto,
InspectConversationRequestDto, LaunchAgentRequestDto, LiveAgentListDto, MemoryDto,
MemoryIndexDto, MemoryLinksDto, MemoryListDto, OpenCodeProviderListDto, OpenTerminalRequestDto,
ProfileDto, ProfileListDto, ProfileModelCatalogDto, ProjectDto, ProjectListDto,
ProjectMcpToolPermissionsDto, ProjectPermissionsDto, ProjectSystemPermissionsDto,
ProjectWorkStateDto, ReadAgentContextResponseDto, ReadConversationPageRequestDto,
RecallMemoryRequestDto, ResolveAgentPermissionsRequestDto,
ResolveAgentSystemPermissionsRequestDto, ResolvedAgentSystemPermissionsDto,
ResumableAgentListDto, SaveEmbedderProfileRequestDto, SaveOpenCodeProviderProfileRequestDto,
SaveProfileRequestDto, SkillDto, SkillListDto, SprintCreateRequestDto, SprintDeleteRequestDto,
SprintDto, SprintListDto, SprintListRequestDto, SprintRenameRequestDto,
SprintReorderRequestDto, StopLiveAgentRequestDto, StopLiveAgentResponseDto,
SyncAgentWithTemplateRequestDto, SyncResultDto, TemplateDto, TemplateListDto,
TerminalSessionDto, TicketAssignRequestDto, TicketAttachmentAddRequestDto,
TicketAttachmentContentDto, TicketAttachmentMarkSummarizedRequestDto,
TicketAttachmentReadRequestDto, TicketCarnetDto, TicketCreateRequestDto,
TicketDeleteRequestDto, TicketDto, TicketLinkCommandRequestDto, TicketListPageInput,
TicketListRequestDto, TicketReadRequestDto, TicketSprintAssignRequestDto,
TicketSprintUnassignRequestDto, TicketUnlinkCommandRequestDto, TicketUpdateCarnetRequestDto,
TicketUpdateRequestDto, TurnPageDto, UnassignSkillRequestDto, UpdateAgentContextRequestDto,
UpdateAgentMcpToolPermissionsRequestDto, UpdateAgentPermissionsRequestDto,
@ -2478,6 +2482,11 @@ async fn invoke(
"ticket_read" => invoke_ticket_read(&request.args, &state.app).await,
"ticket_list" => invoke_ticket_list(&request.args, &state.app).await,
"ticket_update" => invoke_ticket_update(&request.args, &state.app).await,
"ticket_attachment_add" => invoke_ticket_attachment_add(&request.args, &state.app).await,
"ticket_attachment_read" => invoke_ticket_attachment_read(&request.args, &state.app).await,
"ticket_attachment_mark_summarized" => {
invoke_ticket_attachment_mark_summarized(&request.args, &state.app).await
}
"ticket_delete" => invoke_ticket_delete(&request.args, &state.app).await,
"ticket_read_carnet" => invoke_ticket_read_carnet(&request.args, &state.app).await,
"ticket_update_carnet" => invoke_ticket_update_carnet(&request.args, &state.app).await,
@ -3050,6 +3059,72 @@ async fn invoke_ticket_update(args: &Value, state: &BackendCore) -> Result<Value
serde_json::to_value(TicketDto::from_issue(issue, None)).map_err(serialization_error)
}
async fn invoke_ticket_attachment_add(
args: &Value,
state: &BackendCore,
) -> Result<Value, ErrorDto> {
let request = required_request::<TicketAttachmentAddRequestDto>("ticket_attachment_add", args)?;
let project = resolve_project_readonly(&request.project_id, state).await?;
let issue = state
.add_issue_attachment
.execute(AddIssueAttachmentInput {
project,
issue_ref: parse_ref_dto(&request.r#ref)?,
path: request.path,
mime: request.mime,
expected_version: version_dto(request.expected_version)?,
actor: IssueActor::User,
})
.await
.map_err(ErrorDto::from)?
.issue;
serde_json::to_value(TicketDto::from_issue(issue, None)).map_err(serialization_error)
}
async fn invoke_ticket_attachment_read(
args: &Value,
state: &BackendCore,
) -> Result<Value, ErrorDto> {
let request =
required_request::<TicketAttachmentReadRequestDto>("ticket_attachment_read", args)?;
let project = resolve_project_readonly(&request.project_id, state).await?;
let content = state
.read_issue_attachment
.execute(ReadIssueAttachmentInput {
project,
issue_ref: parse_ref_dto(&request.r#ref)?,
attachment_id: parse_attachment_id_dto(&request.attachment_id)?,
})
.await
.map_err(ErrorDto::from)?
.content;
serde_json::to_value(TicketAttachmentContentDto::from(content)).map_err(serialization_error)
}
async fn invoke_ticket_attachment_mark_summarized(
args: &Value,
state: &BackendCore,
) -> Result<Value, ErrorDto> {
let request = required_request::<TicketAttachmentMarkSummarizedRequestDto>(
"ticket_attachment_mark_summarized",
args,
)?;
let project = resolve_project_readonly(&request.project_id, state).await?;
let issue = state
.mark_issue_attachment_summarized
.execute(MarkIssueAttachmentSummarizedInput {
project,
issue_ref: parse_ref_dto(&request.r#ref)?,
attachment_id: parse_attachment_id_dto(&request.attachment_id)?,
expected_version: version_dto(request.expected_version)?,
actor: IssueActor::User,
})
.await
.map_err(ErrorDto::from)?
.issue;
serde_json::to_value(TicketDto::from_issue(issue, None)).map_err(serialization_error)
}
async fn invoke_ticket_delete(args: &Value, state: &BackendCore) -> Result<Value, ErrorDto> {
let request = required_request::<TicketDeleteRequestDto>("ticket_delete", args)?;
let project = resolve_project_readonly(&request.project_id, state).await?;