@downcity/agent 1.1.157 → 1.1.161
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/config/Paths.d.ts +1 -1
- package/bin/config/Paths.js +1 -1
- package/bin/executor/core-engine/CoreEngineMessageState.d.ts +1 -1
- package/bin/executor/core-engine/CoreEngineMessageState.js +1 -1
- package/bin/executor/messages/AssistantFileResource.d.ts +35 -4
- package/bin/executor/messages/AssistantFileResource.d.ts.map +1 -1
- package/bin/executor/messages/AssistantFileResource.js +151 -23
- package/bin/executor/messages/AssistantFileResource.js.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.d.ts +3 -3
- package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.js +17 -18
- package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
- package/bin/executor/messages/SessionMessageCodec.d.ts.map +1 -1
- package/bin/executor/messages/SessionMessageCodec.js +1 -1
- package/bin/executor/messages/SessionMessageCodec.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.js +14 -25
- package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
- package/bin/executor/tools/plugin/types/PluginTool.d.ts +3 -3
- package/bin/executor/tools/plugin/types/PluginTool.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/assistant-file-resource.test.mjs +77 -11
- package/scripts/image-plugin-job.test.mjs +75 -1
- package/scripts/plugin-tool-bridge.test.mjs +4 -4
- package/src/config/Paths.ts +1 -1
- package/src/executor/core-engine/CoreEngineMessageState.ts +1 -1
- package/src/executor/messages/AssistantFileResource.ts +184 -26
- package/src/executor/messages/SessionAttachmentMapper.ts +16 -17
- package/src/executor/messages/SessionMessageCodec.ts +4 -1
- package/src/executor/tools/plugin/PluginToolBridge.ts +17 -27
- package/src/executor/tools/plugin/types/PluginTool.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - 兼容 Telegram / Feishu / TUI 等统一的 `<file>` 协议入口。
|
|
6
6
|
* - 仅在本轮执行的内存消息上追加 file parts,不修改持久化历史。
|
|
7
7
|
* - 当前只为图片与 PDF 注入 file part,保持多模态模型可直接消费。
|
|
8
|
-
* -
|
|
8
|
+
* - 历史中的相对路径与旧版 `file://` 会在喂给模型前临时 hydrate。
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import fs from "fs-extra";
|
|
@@ -63,18 +63,17 @@ function buildDataUrl(mediaType: string, buffer: Buffer): string {
|
|
|
63
63
|
return `data:${safeType};base64,${base64}`;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
function
|
|
66
|
+
function resolveHydratableFilePath(
|
|
67
67
|
projectRoot: string | undefined,
|
|
68
|
-
|
|
68
|
+
rawPath: string,
|
|
69
69
|
): string | null {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
if (!relative) return null;
|
|
70
|
+
const raw = String(rawPath || "").trim();
|
|
71
|
+
if (!raw || raw.startsWith("data:") || /^https?:\/\//i.test(raw)) return null;
|
|
72
|
+
if (raw.startsWith("file://")) return fileURLToPath(raw);
|
|
73
|
+
if (path.isAbsolute(raw)) return path.resolve(raw);
|
|
75
74
|
|
|
76
75
|
const root = path.resolve(String(projectRoot || "").trim() || process.cwd());
|
|
77
|
-
const absPath = path.resolve(root,
|
|
76
|
+
const absPath = path.resolve(root, raw);
|
|
78
77
|
const rel = path.relative(root, absPath);
|
|
79
78
|
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return null;
|
|
80
79
|
return absPath;
|
|
@@ -85,14 +84,13 @@ async function hydrateFileUrlPart(
|
|
|
85
84
|
projectRoot?: string,
|
|
86
85
|
): Promise<FileUIPart> {
|
|
87
86
|
const url = String(part.url || "").trim();
|
|
88
|
-
const
|
|
89
|
-
if (!
|
|
87
|
+
const filePath = resolveHydratableFilePath(projectRoot, url);
|
|
88
|
+
if (!filePath) return part;
|
|
90
89
|
try {
|
|
91
|
-
const
|
|
92
|
-
const buffer = await fs.readFile(absPath);
|
|
90
|
+
const buffer = await fs.readFile(filePath);
|
|
93
91
|
const mediaType =
|
|
94
92
|
String(part.mediaType || "").trim() ||
|
|
95
|
-
guessAttachmentMediaTypeFromPath(
|
|
93
|
+
guessAttachmentMediaTypeFromPath(filePath) ||
|
|
96
94
|
"application/octet-stream";
|
|
97
95
|
return {
|
|
98
96
|
...part,
|
|
@@ -109,7 +107,7 @@ async function hydrateFileUrlPart(
|
|
|
109
107
|
*
|
|
110
108
|
* 关键点(中文)
|
|
111
109
|
* - 该函数只修改本轮内存消息,不回写历史。
|
|
112
|
-
* - 新历史保留
|
|
110
|
+
* - 新历史保留 Agent 根目录相对路径,旧历史的 `file://` 仍继续兼容。
|
|
113
111
|
*/
|
|
114
112
|
export async function hydrateFileUrlPartsForModel(
|
|
115
113
|
messages: SessionMessageV1[],
|
|
@@ -148,10 +146,11 @@ export async function hydrateFileUrlPartsForModel(
|
|
|
148
146
|
*/
|
|
149
147
|
export async function injectFilePartsFromAttachments(
|
|
150
148
|
messages: SessionMessageV1[],
|
|
149
|
+
projectRoot?: string,
|
|
151
150
|
): Promise<SessionMessageV1[]> {
|
|
152
151
|
if (!Array.isArray(messages) || messages.length === 0) return messages;
|
|
153
152
|
|
|
154
|
-
const
|
|
153
|
+
const root = path.resolve(String(projectRoot || "").trim() || process.cwd());
|
|
155
154
|
const out: SessionMessageV1[] = [];
|
|
156
155
|
|
|
157
156
|
for (const message of messages) {
|
|
@@ -203,7 +202,7 @@ export async function injectFilePartsFromAttachments(
|
|
|
203
202
|
|
|
204
203
|
const absPath = path.isAbsolute(attachment.path)
|
|
205
204
|
? attachment.path
|
|
206
|
-
: path.resolve(
|
|
205
|
+
: path.resolve(root, attachment.path);
|
|
207
206
|
try {
|
|
208
207
|
const exists = await fs.pathExists(absPath);
|
|
209
208
|
if (!exists) continue;
|
|
@@ -78,7 +78,10 @@ export async function toModelMessages(
|
|
|
78
78
|
if (!Array.isArray(messages) || messages.length === 0) return [];
|
|
79
79
|
|
|
80
80
|
// 第一步(中文):在 user 消息上注入 file parts(多模态附件)。
|
|
81
|
-
const enrichedMessages = await injectFilePartsFromAttachments(
|
|
81
|
+
const enrichedMessages = await injectFilePartsFromAttachments(
|
|
82
|
+
messages,
|
|
83
|
+
projectRoot,
|
|
84
|
+
);
|
|
82
85
|
|
|
83
86
|
// 第二步(中文):把历史里的资源 URL 在内存中 hydrate 成模型可消费的 data URL。
|
|
84
87
|
const hydratedMessages = await hydrateFileUrlPartsForModel(
|
|
@@ -18,7 +18,10 @@ import type {
|
|
|
18
18
|
PluginReadInput,
|
|
19
19
|
PluginReadToolResult,
|
|
20
20
|
} from "@/executor/tools/plugin/types/PluginTool.js";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
materializeAssistantFileParts,
|
|
23
|
+
resolveAgentFilePath,
|
|
24
|
+
} from "@executor/messages/AssistantFileResource.js";
|
|
22
25
|
import {
|
|
23
26
|
enqueueAssistantFileParts,
|
|
24
27
|
getSessionRunContext,
|
|
@@ -81,25 +84,6 @@ function resolve_project_root(project_root: string | undefined): string {
|
|
|
81
84
|
return path.resolve(raw || process.cwd());
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
/**
|
|
85
|
-
* 将 `resources://` URL 转成本机绝对路径。
|
|
86
|
-
*/
|
|
87
|
-
function resolve_resources_file_path(
|
|
88
|
-
project_root: string,
|
|
89
|
-
raw_url: string,
|
|
90
|
-
): string {
|
|
91
|
-
const prefix = "resources://";
|
|
92
|
-
const raw = String(raw_url || "").trim();
|
|
93
|
-
if (!raw.startsWith(prefix)) return "";
|
|
94
|
-
const relative = raw.slice(prefix.length).replace(/^\/+/, "");
|
|
95
|
-
if (!relative) return "";
|
|
96
|
-
|
|
97
|
-
const file_path = path.resolve(project_root, relative);
|
|
98
|
-
const rel = path.relative(project_root, file_path);
|
|
99
|
-
if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return "";
|
|
100
|
-
return file_path;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
87
|
/**
|
|
104
88
|
* 构建返回给模型和用户可见的文件摘要。
|
|
105
89
|
*/
|
|
@@ -107,13 +91,19 @@ function summarize_materialized_files(
|
|
|
107
91
|
parts: FileUIPart[],
|
|
108
92
|
project_root: string,
|
|
109
93
|
): PluginCallToolFileResult[] {
|
|
110
|
-
return parts.map((part, index) =>
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
94
|
+
return parts.map((part, index) => {
|
|
95
|
+
const relative_path = String(part.url || "");
|
|
96
|
+
return {
|
|
97
|
+
index,
|
|
98
|
+
media_type: part.mediaType,
|
|
99
|
+
filename: typeof part.filename === "string" ? part.filename : "",
|
|
100
|
+
relative_path,
|
|
101
|
+
path: resolveAgentFilePath({
|
|
102
|
+
projectRoot: project_root,
|
|
103
|
+
filePath: relative_path,
|
|
104
|
+
}),
|
|
105
|
+
};
|
|
106
|
+
});
|
|
117
107
|
}
|
|
118
108
|
|
|
119
109
|
/**
|
|
@@ -40,9 +40,9 @@ export interface PluginCallToolFileResult {
|
|
|
40
40
|
media_type: string;
|
|
41
41
|
/** 原始文件名;若上游未提供则为空字符串。 */
|
|
42
42
|
filename: string;
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
/**
|
|
43
|
+
/** 基于 Agent 项目根目录的相对路径,例如 `.downcity/resources/xxx.png`。 */
|
|
44
|
+
relative_path: string;
|
|
45
|
+
/** 当前机器可直接打开的绝对文件路径,按 Agent 项目根目录解析。 */
|
|
46
46
|
path: string;
|
|
47
47
|
}
|
|
48
48
|
|