feat: 主 Agent 对话支持文件上传(方式1:落盘 + Read/Bash 读取)
- 前置:task 工作目录 <workDir>/<taskID> → <workDir>/tasks/<taskID>(改中心
ensureRunDir,与 sessions/<id> 对称;三 agent 全走它)。
- 上传接口(方案B,一个/两 scope):POST /api/chat/upload?scope=task|session&id=X,
落到 tasks|sessions/<id>/uploads/,路径安全化 + 同名加序号,返回 {name,path,size}。
- 后端注入(task main agent):chat handler 收 attachments;user 活动 Detail 存
{text,attachments} JSON 供消息渲染;发给 agent 的清单用【绝对路径】(baseDir+相对,
与落盘/CWD 完全对齐)保证一定 Read 得到。
- 前端(会话与任务共用 transcript 渲染):
· api.chatUpload + chat(attachments) + ChatAttachment 类型;
· transcript UserRow 解析 Detail JSON,气泡上方渲染附件卡片(乐观回显内联 detail);
· sessions-tab 输入区加回形针上传按钮 + 发送前 chip 预览(可移除)+ 带附件发送。
- 说明:文本/代码/二进制经 Read/Bash 处理;图片视觉不支持(norma 无 image block)。
会话(ChatAgent)面暂只共用了渲染,后端注入 + Composer 上传待后续补。
This commit is contained in:
1 parent
6348ec9d21
commit
071414063e
9 files changed
+337
-39
No files matched your search
+17
-2
@@ -9,7 +9,7 @@ import type {
|
||||
Task, Stats, Asset, AssetNode, Edge, Company, TaskNode, Finding, Activity,
|
||||
Audit, TrafficResp, TrafficDetail, TrafficHost, Settings, LLMProfile, Agent, AgentDetail, PromptVar,
|
||||
PromptVersion, MCPServer, MCPTool, SkillItem, TokenUsage, TokenTotal, DailyTokenBucket,
|
||||
Tool, Conversation, AgentTrigger,
|
||||
Tool, Conversation, AgentTrigger, ChatAttachment,
|
||||
InterceptRule, InterceptPending, InterceptApprovalRow,
|
||||
TaskAssetView, SSProject, SSTask, ConvTokenSummary, CoverageGraphData, CoverageAssetRefs,
|
||||
WorkspaceListing, WorkspaceFile,
|
||||
@@ -315,7 +315,22 @@ export const api = {
|
||||
if (!r.ok) throw new Error(`report: ${r.status}`);
|
||||
return r.text();
|
||||
},
|
||||
chat: (message: string, task?: string) => post<{ reply: string; mode: string }>(`/chat${tq(task)}`, { message }),
|
||||
chat: (message: string, task?: string, attachments?: ChatAttachment[]) =>
|
||||
post<{ reply: string; mode: string }>(`/chat${tq(task)}`, { message, attachments }),
|
||||
// 方式1 文件上传:落到会话/任务工作目录 uploads/,返回可供 agent Read 的相对路径。
|
||||
chatUpload: async (scope: "task" | "session", id: string, files: File[]) => {
|
||||
if (MOCK) return { attachments: files.map((f) => ({ name: f.name, path: `uploads/${f.name}`, size: f.size })) };
|
||||
const fd = new FormData();
|
||||
for (const f of files) fd.append("file", f);
|
||||
const token = getToken();
|
||||
const r = await fetch(`/api/chat/upload?scope=${scope}&id=${encodeURIComponent(id)}`, {
|
||||
method: "POST",
|
||||
headers: { ...(token ? { Authorization: `Bearer ${token}` } : {}) },
|
||||
body: fd,
|
||||
});
|
||||
if (!r.ok) throw new Error(`上传失败: ${r.status} ${await r.text()}`);
|
||||
return r.json() as Promise<{ attachments: ChatAttachment[] }>;
|
||||
},
|
||||
stopChat: (taskId: string) => post<{ status: string }>(`/tasks/${taskId}/chat/stop`, {}),
|
||||
gc: (ttl = 86400) => post<{ removed: number }>(`/gc?ttl=${ttl}`, {}),
|
||||
|
||||
|
||||
@@ -265,6 +265,13 @@ export type ActivityKind =
|
||||
| "usage" // live cumulative token usage (per model turn); not rendered
|
||||
| "intercept_request"; // user-approval request from the intercept layer
|
||||
|
||||
// ChatAttachment 是一次上传的文件:path 相对该会话/任务工作目录(即 agent 的 CWD)。
|
||||
export interface ChatAttachment {
|
||||
name: string;
|
||||
path: string;
|
||||
size: number;
|
||||
}
|
||||
|
||||
export interface Activity {
|
||||
seq: number;
|
||||
intent_id?: string;
|
||||
|
||||
Reference in new issue
Block a user