@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.
Files changed (32) hide show
  1. package/bin/config/Paths.d.ts +1 -1
  2. package/bin/config/Paths.js +1 -1
  3. package/bin/executor/core-engine/CoreEngineMessageState.d.ts +1 -1
  4. package/bin/executor/core-engine/CoreEngineMessageState.js +1 -1
  5. package/bin/executor/messages/AssistantFileResource.d.ts +35 -4
  6. package/bin/executor/messages/AssistantFileResource.d.ts.map +1 -1
  7. package/bin/executor/messages/AssistantFileResource.js +151 -23
  8. package/bin/executor/messages/AssistantFileResource.js.map +1 -1
  9. package/bin/executor/messages/SessionAttachmentMapper.d.ts +3 -3
  10. package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
  11. package/bin/executor/messages/SessionAttachmentMapper.js +17 -18
  12. package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
  13. package/bin/executor/messages/SessionMessageCodec.d.ts.map +1 -1
  14. package/bin/executor/messages/SessionMessageCodec.js +1 -1
  15. package/bin/executor/messages/SessionMessageCodec.js.map +1 -1
  16. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
  17. package/bin/executor/tools/plugin/PluginToolBridge.js +14 -25
  18. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
  19. package/bin/executor/tools/plugin/types/PluginTool.d.ts +3 -3
  20. package/bin/executor/tools/plugin/types/PluginTool.d.ts.map +1 -1
  21. package/package.json +3 -3
  22. package/scripts/assistant-file-resource.test.mjs +77 -11
  23. package/scripts/image-plugin-job.test.mjs +75 -1
  24. package/scripts/plugin-tool-bridge.test.mjs +4 -4
  25. package/src/config/Paths.ts +1 -1
  26. package/src/executor/core-engine/CoreEngineMessageState.ts +1 -1
  27. package/src/executor/messages/AssistantFileResource.ts +184 -26
  28. package/src/executor/messages/SessionAttachmentMapper.ts +16 -17
  29. package/src/executor/messages/SessionMessageCodec.ts +4 -1
  30. package/src/executor/tools/plugin/PluginToolBridge.ts +17 -27
  31. package/src/executor/tools/plugin/types/PluginTool.ts +3 -3
  32. package/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"PluginToolBridge.js","sourceRoot":"","sources":["../../../../src/executor/tools/plugin/PluginToolBridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PluginToolBridge.js","sourceRoot":"","sources":["../../../../src/executor/tools/plugin/PluginToolBridge.ts"],"names":[],"mappings":"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"}
@@ -36,9 +36,9 @@ export interface PluginCallToolFileResult {
36
36
  media_type: string;
37
37
  /** 原始文件名;若上游未提供则为空字符串。 */
38
38
  filename: string;
39
- /** 持久化到历史消息中的资源 URL,通常为 `resources://.downcity/resources/...`。 */
40
- url: string;
41
- /** 当前机器可直接打开的绝对文件路径。 */
39
+ /** 基于 Agent 项目根目录的相对路径,例如 `.downcity/resources/xxx.png`。 */
40
+ relative_path: string;
41
+ /** 当前机器可直接打开的绝对文件路径,按 Agent 项目根目录解析。 */
42
42
  path: string;
43
43
  }
44
44
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"PluginTool.d.ts","sourceRoot":"","sources":["../../../../../src/executor/tools/plugin/types/PluginTool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sCAAsC;IACtC,KAAK,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACnC,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB"}
1
+ {"version":3,"file":"PluginTool.d.ts","sourceRoot":"","sources":["../../../../../src/executor/tools/plugin/types/PluginTool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,uCAAuC;IACvC,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,aAAa,EAAE,MAAM,CAAC;IACtB,wCAAwC;IACxC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,kDAAkD;IAClD,oBAAoB,EAAE,MAAM,CAAC;IAC7B,sCAAsC;IACtC,KAAK,CAAC,EAAE,wBAAwB,EAAE,CAAC;IACnC,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qBAAqB;IACrB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,cAAc;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,qBAAqB;IACrB,IAAI,EAAE,UAAU,CAAC;CAClB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/agent",
3
- "version": "1.1.157",
3
+ "version": "1.1.161",
4
4
  "type": "module",
5
5
  "description": "Downcity Agent 运行时 — 单 Agent 执行壳与本机 RPC 能力",
6
6
  "main": "./bin/index.js",
@@ -17,8 +17,8 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@ai-sdk/openai-compatible": "^2.0.48",
20
- "@downcity/shell": "^0.1.33",
21
- "@downcity/type": "0.1.67",
20
+ "@downcity/shell": "^0.1.36",
21
+ "@downcity/type": "0.1.70",
22
22
  "@larksuiteoapi/node-sdk": "^1.66.0",
23
23
  "ai": "^6.0.193",
24
24
  "commander": "^15.0.0",
@@ -1,9 +1,9 @@
1
1
  /**
2
- * @file 验证 assistant file part 会把资源落盘为 resources:// URL。
2
+ * @file 验证 assistant file part 会把资源落盘为 Agent 根目录相对路径。
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 历史消息不应长期保存图片 base64。
6
- * - 送模型前可以从 resources:// 临时 hydrate 回 data URL。
6
+ * - 送模型前可以从 Agent 根目录相对路径临时 hydrate 回 data URL。
7
7
  */
8
8
 
9
9
  import test from "node:test";
@@ -15,10 +15,13 @@ import http from "node:http";
15
15
  import { pathToFileURL } from "node:url";
16
16
 
17
17
  import { materializeAssistantFileParts } from "../bin/executor/messages/AssistantFileResource.js";
18
- import { hydrateFileUrlPartsForModel } from "../bin/executor/messages/SessionAttachmentMapper.js";
18
+ import {
19
+ hydrateFileUrlPartsForModel,
20
+ injectFilePartsFromAttachments,
21
+ } from "../bin/executor/messages/SessionAttachmentMapper.js";
19
22
 
20
- function resource_path_from_url(project_root, url) {
21
- return path.join(project_root, String(url || "").replace(/^resources:\/\//, ""));
23
+ function resource_path_from_relative_path(project_root, relative_path) {
24
+ return path.join(project_root, String(relative_path || ""));
22
25
  }
23
26
 
24
27
  test("materializeAssistantFileParts stores data URL images under .downcity/resources", async () => {
@@ -44,10 +47,10 @@ test("materializeAssistantFileParts stores data URL images under .downcity/resou
44
47
  assert.equal(parts[0].type, "file");
45
48
  assert.equal(parts[0].mediaType, "image/png");
46
49
  assert.equal(parts[0].filename, "image-1.png");
47
- assert.match(parts[0].url, /^resources:\/\/\.downcity\/resources\//);
50
+ assert.match(parts[0].url, /^\.downcity\/resources\//);
48
51
  assert.equal(parts[0].url.includes("base64"), false);
49
52
 
50
- const resource_path = resource_path_from_url(project_root, parts[0].url);
53
+ const resource_path = resource_path_from_relative_path(project_root, parts[0].url);
51
54
  assert.equal(
52
55
  path.dirname(resource_path),
53
56
  path.join(project_root, ".downcity", "resources"),
@@ -84,10 +87,10 @@ test("materializeAssistantFileParts downloads remote file URLs into resources",
84
87
  ],
85
88
  });
86
89
 
87
- assert.match(parts[0].url, /^resources:\/\/\.downcity\/resources\//);
90
+ assert.match(parts[0].url, /^\.downcity\/resources\//);
88
91
  assert.equal(parts[0].url.startsWith("http://"), false);
89
92
  assert.deepEqual(
90
- await fs.readFile(resource_path_from_url(project_root, parts[0].url)),
93
+ await fs.readFile(resource_path_from_relative_path(project_root, parts[0].url)),
91
94
  bytes,
92
95
  );
93
96
  } finally {
@@ -97,7 +100,33 @@ test("materializeAssistantFileParts downloads remote file URLs into resources",
97
100
  }
98
101
  });
99
102
 
100
- test("hydrateFileUrlPartsForModel converts resources URLs back to data URLs in memory", async () => {
103
+ test("materializeAssistantFileParts resolves relative local file URLs from agent project root", async () => {
104
+ const project_root = await fs.mkdtemp(
105
+ path.join(os.tmpdir(), "downcity-agent-assistant-relative-resource-"),
106
+ );
107
+ const bytes = Buffer.from("relative-local-png-bytes", "utf8");
108
+ await fs.writeFile(path.join(project_root, "input.png"), bytes);
109
+
110
+ const parts = await materializeAssistantFileParts({
111
+ projectRoot: project_root,
112
+ parts: [
113
+ {
114
+ type: "file",
115
+ mediaType: "image/png",
116
+ filename: "input.png",
117
+ url: "./input.png",
118
+ },
119
+ ],
120
+ });
121
+
122
+ assert.match(parts[0].url, /^\.downcity\/resources\//);
123
+ assert.deepEqual(
124
+ await fs.readFile(resource_path_from_relative_path(project_root, parts[0].url)),
125
+ bytes,
126
+ );
127
+ });
128
+
129
+ test("hydrateFileUrlPartsForModel converts relative resource paths back to data URLs in memory", async () => {
101
130
  const project_root = await fs.mkdtemp(
102
131
  path.join(os.tmpdir(), "downcity-agent-assistant-hydrate-"),
103
132
  );
@@ -137,7 +166,7 @@ test("hydrateFileUrlPartsForModel converts resources URLs back to data URLs in m
137
166
  hydrated_part?.url,
138
167
  `data:image/png;base64,${bytes.toString("base64")}`,
139
168
  );
140
- assert.match(materialized[0].url, /^resources:\/\/\.downcity\/resources\//);
169
+ assert.match(materialized[0].url, /^\.downcity\/resources\//);
141
170
  });
142
171
 
143
172
  test("hydrateFileUrlPartsForModel keeps old file URLs compatible", async () => {
@@ -178,3 +207,40 @@ test("hydrateFileUrlPartsForModel keeps old file URLs compatible", async () => {
178
207
  `data:image/png;base64,${bytes.toString("base64")}`,
179
208
  );
180
209
  });
210
+
211
+ test("injectFilePartsFromAttachments resolves relative file tags from agent project root", async () => {
212
+ const project_root = await fs.mkdtemp(
213
+ path.join(os.tmpdir(), "downcity-agent-attachment-relative-"),
214
+ );
215
+ const bytes = Buffer.from("attachment-relative-png-bytes", "utf8");
216
+ await fs.writeFile(path.join(project_root, "input.png"), bytes);
217
+
218
+ const messages = await injectFilePartsFromAttachments(
219
+ [
220
+ {
221
+ id: "u:test:attachment",
222
+ role: "user",
223
+ metadata: {
224
+ v: 1,
225
+ ts: Date.now(),
226
+ sessionId: "session_test",
227
+ },
228
+ parts: [
229
+ {
230
+ type: "text",
231
+ text: '<file type="photo">./input.png</file>',
232
+ },
233
+ ],
234
+ },
235
+ ],
236
+ project_root,
237
+ );
238
+
239
+ const injected_part = messages[0]?.parts[1];
240
+ assert.equal(injected_part?.type, "file");
241
+ assert.equal(injected_part?.mediaType, "image/png");
242
+ assert.equal(
243
+ injected_part?.url,
244
+ `data:image/png;base64,${bytes.toString("base64")}`,
245
+ );
246
+ });
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 插件只暴露 image_create / image_result 两个任务 action。
6
- * - image_result 只读取一次当前状态,不在 plugin 层等待终态。
6
+ * - image_result 默认只读取一次当前状态;传 until_done=true 时会在 plugin 层等待终态。
7
7
  * - 成功图片仍以 UIMessage 返回,后续由 plugin bridge 落盘 file parts。
8
8
  */
9
9
 
@@ -132,6 +132,7 @@ test("ImagePlugin exposes action metadata through plugin registry", async () =>
132
132
  assert.equal(metadata.actions[0].name, "image_create");
133
133
  assert.equal(metadata.actions[0].has_input_schema, true);
134
134
  assert.match(metadata.actions[0].description, /Create an async image job/);
135
+ assert.match(metadata.actions[0].description, /explicit user confirmation/);
135
136
  assert.equal(metadata.actions[0].examples[0].payload.prompt.includes("rainy city"), true);
136
137
  });
137
138
 
@@ -354,3 +355,76 @@ test("ImagePlugin image_create rejects legacy messages and data URLs", async ()
354
355
  assert.equal(data_url_result.success, false);
355
356
  assert.match(data_url_result.error, /does not accept data URLs/);
356
357
  });
358
+
359
+ test("ImagePlugin image_result polls until terminal when until_done=true", async () => {
360
+ const calls = [];
361
+ let next_status = "running";
362
+ const message = create_image_message();
363
+ const plugin = new ImagePlugin({
364
+ image_create: () => ({ job_id: "img_wait", status: "queued", poll_after_ms: 1 }),
365
+ image_result: (input) => {
366
+ calls.push(input.job_id);
367
+ if (calls.length >= 3) {
368
+ next_status = "succeeded";
369
+ }
370
+ if (next_status === "succeeded") {
371
+ return {
372
+ job_id: input.job_id,
373
+ status: "succeeded",
374
+ result: message,
375
+ poll_after_ms: 1,
376
+ };
377
+ }
378
+ return {
379
+ job_id: input.job_id,
380
+ status: "running",
381
+ message: "still going",
382
+ poll_after_ms: 1,
383
+ };
384
+ },
385
+ });
386
+
387
+ const result = await plugin.actions.image_result.execute({
388
+ context: create_context(),
389
+ payload: {
390
+ job_id: "img_wait",
391
+ until_done: true,
392
+ max_wait_ms: 500,
393
+ poll_interval_ms: 5,
394
+ },
395
+ pluginName: "image",
396
+ actionName: "image_result",
397
+ });
398
+
399
+ assert.equal(result.success, true);
400
+ assert.equal(result.data.role, "assistant");
401
+ assert.ok(calls.length >= 3);
402
+ });
403
+
404
+ test("ImagePlugin image_result returns last status when max_wait_ms elapses", async () => {
405
+ const plugin = new ImagePlugin({
406
+ image_create: () => ({ job_id: "img_timeout", status: "queued", poll_after_ms: 1 }),
407
+ image_result: (input) => ({
408
+ job_id: input.job_id,
409
+ status: "running",
410
+ message: "always pending",
411
+ poll_after_ms: 1,
412
+ }),
413
+ });
414
+
415
+ const result = await plugin.actions.image_result.execute({
416
+ context: create_context(),
417
+ payload: {
418
+ job_id: "img_timeout",
419
+ until_done: true,
420
+ max_wait_ms: 30,
421
+ poll_interval_ms: 5,
422
+ },
423
+ pluginName: "image",
424
+ actionName: "image_result",
425
+ });
426
+
427
+ assert.equal(result.success, true);
428
+ assert.equal(result.data.status, "running");
429
+ assert.equal(result.data.message, "always pending");
430
+ });
@@ -2,8 +2,8 @@
2
2
  * @file 验证 plugin tool bridge 会把生成文件摘要为可打开路径。
3
3
  *
4
4
  * 关键点(中文)
5
- * - assistant message 仍然使用 `resources://` URL,避免历史暴露本机路径。
6
- * - tool result 额外返回本机绝对路径,便于模型与用户明确知道文件位置。
5
+ * - assistant message 使用 Agent 根目录相对路径,避免历史暴露本机路径。
6
+ * - tool result 同时返回相对路径与本机绝对路径,便于模型与用户明确知道文件位置。
7
7
  */
8
8
 
9
9
  import test from "node:test";
@@ -78,7 +78,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
78
78
  assert.equal(result.success, true);
79
79
  assert.equal(result.assistant_file_count, 1);
80
80
  assert.equal(result.files?.length, 1);
81
- assert.match(result.files[0].url, /^resources:\/\/\.downcity\/resources\//);
81
+ assert.match(result.files[0].relative_path, /^\.downcity\/resources\//);
82
82
  assert.equal(path.isAbsolute(result.files[0].path), true);
83
83
  assert.equal(
84
84
  path.dirname(result.files[0].path),
@@ -89,7 +89,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
89
89
 
90
90
  const pending_parts = run_context.pendingAssistantFileParts;
91
91
  assert.equal(pending_parts.length, 1);
92
- assert.equal(pending_parts[0].url, result.files[0].url);
92
+ assert.equal(pending_parts[0].url, result.files[0].relative_path);
93
93
  });
94
94
 
95
95
  test("invokePluginReadTool returns plugin action metadata", async () => {
@@ -336,7 +336,7 @@ export function getDowncityPublicDirPath(cwd: string): string {
336
336
  *
337
337
  * 关键点(中文)
338
338
  * - 该目录用于存放会话历史引用的二进制资源,例如图片生成结果。
339
- * - `messages.jsonl` 只保存 `resources://` 相对 URL,避免暴露本机绝对路径或长期保存 base64。
339
+ * - `messages.jsonl` 只保存 Agent 根目录相对路径,避免暴露本机绝对路径或长期保存 base64。
340
340
  */
341
341
  export function getDowncityResourcesDirPath(cwd: string): string {
342
342
  return path.join(getDowncityDirPath(cwd), "resources");
@@ -34,7 +34,7 @@ export class CoreEngineMessageState {
34
34
  private readonly tools: Record<string, Tool>;
35
35
 
36
36
  /**
37
- * 当前项目根目录,用于解析历史中的 `resources://` file part。
37
+ * 当前项目根目录,用于解析历史中的相对路径 file part。
38
38
  */
39
39
  private readonly projectRoot?: string;
40
40
 
@@ -4,7 +4,7 @@
4
4
  * 关键点(中文)
5
5
  * - 只处理运行期产生的 assistant file part,不参与 user 附件注入。
6
6
  * - 将 data URL、远程 URL 与本地文件统一写入 `.downcity/resources`。
7
- * - 历史中只保留 `resources://<project-relative-path>`,避免暴露本机绝对路径。
7
+ * - 历史中只保留基于 Agent 项目根目录的相对路径,避免暴露本机绝对路径。
8
8
  * - 资源文件按内容 hash 命名,天然去重并避免重复写入大文件。
9
9
  */
10
10
 
@@ -34,8 +34,8 @@ export interface MaterializeAssistantFilePartsParams {
34
34
  * 当前项目根目录。
35
35
  *
36
36
  * 关键点(中文)
37
- * - 正常 session run 会显式传入 projectRoot。
38
- * - 旧入口未传时回退到 `process.cwd()`,保证行为可用。
37
+ * - 正常 session run 必须显式传入 projectRoot。
38
+ * - 旧入口未传时仅为兼容回退到 `process.cwd()`。
39
39
  */
40
40
  projectRoot?: string;
41
41
 
@@ -102,12 +102,58 @@ function filename_from_url(raw_url: string): string | undefined {
102
102
  }
103
103
  }
104
104
 
105
- function to_resources_url(projectRoot: string, filePath: string): string {
105
+ /**
106
+ * 将项目内文件路径转换为基于 Agent 根目录的相对路径。
107
+ */
108
+ export function toAgentRelativePath(params: {
109
+ /**
110
+ * 当前 Agent 项目根目录。
111
+ */
112
+ projectRoot: string;
113
+ /**
114
+ * 项目内文件绝对路径。
115
+ */
116
+ filePath: string;
117
+ }): string {
118
+ const project_root = resolve_project_root(params.projectRoot);
106
119
  const relative = path
107
- .relative(projectRoot, filePath)
120
+ .relative(project_root, params.filePath)
108
121
  .split(path.sep)
109
122
  .join("/");
110
- return `resources://${relative}`;
123
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
124
+ throw new Error(`Assistant resource path is outside agent root: ${params.filePath}`);
125
+ }
126
+ return relative;
127
+ }
128
+
129
+ /**
130
+ * 将相对路径解析为 Agent 项目内绝对路径。
131
+ *
132
+ * 关键点(中文)
133
+ * - 相对路径统一基于 Agent 项目根目录解析。
134
+ * - 绝对路径原样归一化后返回。
135
+ * - 越界路径返回空字符串,由调用方决定是否忽略。
136
+ */
137
+ export function resolveAgentFilePath(params: {
138
+ /**
139
+ * 当前 Agent 项目根目录。
140
+ */
141
+ projectRoot: string;
142
+ /**
143
+ * 待解析的相对路径或绝对路径。
144
+ */
145
+ filePath: string;
146
+ }): string {
147
+ const raw = String(params.filePath || "").trim();
148
+ if (!raw) return "";
149
+ const project_root = resolve_project_root(params.projectRoot);
150
+ const file_path = path.isAbsolute(raw)
151
+ ? path.resolve(raw)
152
+ : path.resolve(project_root, raw);
153
+ if (path.isAbsolute(raw)) return file_path;
154
+ const rel = path.relative(project_root, file_path);
155
+ if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return "";
156
+ return file_path;
111
157
  }
112
158
 
113
159
  async function write_resource_file(params: {
@@ -140,22 +186,92 @@ async function read_remote_resource(raw_url: string): Promise<{
140
186
  filename?: string;
141
187
  bytes: Buffer;
142
188
  }> {
143
- const response = await fetch(raw_url);
144
- if (!response.ok) {
145
- throw new Error(`Failed to download assistant file resource: ${response.status}`);
189
+ const result = await fetch_with_retry(raw_url);
190
+ if (!result.ok) {
191
+ throw new Error(
192
+ `Failed to download assistant file resource: ${result.status} :: url=${raw_url}`,
193
+ );
146
194
  }
147
- const array_buffer = await response.arrayBuffer();
195
+ const array_buffer = await result.arrayBuffer();
148
196
  const bytes = Buffer.from(array_buffer);
149
197
  if (bytes.length === 0) {
150
- throw new Error("Downloaded assistant file resource is empty");
198
+ throw new Error(`Downloaded assistant file resource is empty :: url=${raw_url}`);
151
199
  }
152
200
  return {
153
- mediaType: response.headers.get("content-type")?.split(";")[0]?.trim(),
201
+ mediaType: result.headers.get("content-type")?.split(";")[0]?.trim(),
154
202
  filename: filename_from_url(raw_url),
155
203
  bytes,
156
204
  };
157
205
  }
158
206
 
207
+ /**
208
+ * 带重试的 fetch 远程资源。
209
+ *
210
+ * 关键点(中文)
211
+ * - 对 transient 网络错误(fetch failed / UND_ERR_* / ECONNRESET 等)做 2 次指数退避重试。
212
+ * - 最终失败时把 error.cause 链展开到 message,方便定位是代理问题、DNS 问题还是上游问题。
213
+ */
214
+ async function fetch_with_retry(
215
+ raw_url: string,
216
+ ): Promise<Response> {
217
+ const delays_ms = [250, 1_000];
218
+ const attempts = delays_ms.length + 1;
219
+ let last_error: unknown;
220
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
221
+ try {
222
+ return await fetch(raw_url);
223
+ } catch (error) {
224
+ last_error = error;
225
+ if (!is_transient_fetch_error(error) || attempt === attempts - 1) {
226
+ throw enrich_fetch_error(error, raw_url);
227
+ }
228
+ const delay_ms = delays_ms[attempt] ?? 1_000;
229
+ await new Promise((resolve) => setTimeout(resolve, delay_ms));
230
+ }
231
+ }
232
+ throw enrich_fetch_error(last_error, raw_url);
233
+ }
234
+
235
+ function is_transient_fetch_error(error: unknown): boolean {
236
+ if (!(error instanceof Error)) return false;
237
+ const text = `${error.message} ${describe_error_cause(error)}`;
238
+ return /fetch failed|UND_ERR|ECONN(RESET|REFUSED|ABORTED)|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up/i.test(
239
+ text,
240
+ );
241
+ }
242
+
243
+ function enrich_fetch_error(error: unknown, url: string): Error {
244
+ if (!(error instanceof Error)) {
245
+ return new Error(`Download failed: ${String(error)} :: url=${url}`);
246
+ }
247
+ const cause_text = describe_error_cause(error);
248
+ const enriched = new Error(
249
+ `${error.message}${cause_text ? ` :: cause=${cause_text}` : ""} :: url=${url}`,
250
+ error.cause ? { cause: error.cause } : undefined,
251
+ );
252
+ enriched.stack = error.stack;
253
+ return enriched;
254
+ }
255
+
256
+ function describe_error_cause(error: Error): string {
257
+ const parts: string[] = [];
258
+ let current: unknown = (error as { cause?: unknown }).cause;
259
+ let depth = 0;
260
+ while (current && depth < 3) {
261
+ if (current instanceof Error) {
262
+ const code = (current as { code?: unknown }).code;
263
+ const code_text = typeof code === "string" && code ? `[${code}] ` : "";
264
+ parts.push(`${code_text}${current.message || current.name}`.trim());
265
+ current = (current as { cause?: unknown }).cause;
266
+ } else {
267
+ parts.push(String(current));
268
+ break;
269
+ }
270
+ depth += 1;
271
+ }
272
+ return parts.filter(Boolean).join(" -> ");
273
+ }
274
+
159
275
  async function read_local_resource(
160
276
  projectRoot: string,
161
277
  raw_url: string,
@@ -188,22 +304,62 @@ async function materialize_file_part(params: {
188
304
  if (!raw_url) {
189
305
  throw new Error("Assistant file part url is required");
190
306
  }
191
- if (raw_url.startsWith("resources://")) return params.part;
192
-
193
307
  const parsed_data_url = parse_data_url(raw_url);
194
- const source = parsed_data_url
195
- ? {
196
- mediaType: parsed_data_url.media_type,
308
+ if (parsed_data_url) {
309
+ const media_type =
310
+ String(params.part.mediaType || parsed_data_url.media_type || "").trim() ||
311
+ "application/octet-stream";
312
+ const file_path = await write_resource_file({
313
+ projectRoot: params.projectRoot,
314
+ mediaType: media_type,
315
+ filename:
316
+ typeof params.part.filename === "string" ? params.part.filename : undefined,
317
+ bytes: parsed_data_url.bytes,
318
+ });
319
+ return {
320
+ ...params.part,
321
+ mediaType: media_type,
322
+ url: toAgentRelativePath({
323
+ projectRoot: params.projectRoot,
324
+ filePath: file_path,
325
+ }),
326
+ };
327
+ }
328
+
329
+ if (raw_url.startsWith("http://") || raw_url.startsWith("https://")) {
330
+ try {
331
+ const source = await read_remote_resource(raw_url);
332
+ const media_type =
333
+ String(params.part.mediaType || source.mediaType || "").trim() ||
334
+ "application/octet-stream";
335
+ const file_path = await write_resource_file({
336
+ projectRoot: params.projectRoot,
337
+ mediaType: media_type,
197
338
  filename:
198
339
  typeof params.part.filename === "string"
199
340
  ? params.part.filename
200
- : undefined,
201
- bytes: parsed_data_url.bytes,
202
- }
203
- : raw_url.startsWith("http://") || raw_url.startsWith("https://")
204
- ? await read_remote_resource(raw_url)
205
- : await read_local_resource(params.projectRoot, raw_url);
341
+ : source.filename,
342
+ bytes: source.bytes,
343
+ });
344
+ return {
345
+ ...params.part,
346
+ mediaType: media_type,
347
+ url: toAgentRelativePath({
348
+ projectRoot: params.projectRoot,
349
+ filePath: file_path,
350
+ }),
351
+ };
352
+ } catch (error) {
353
+ // 关键点(中文):远程下载失败时保留原始 URL,不让整张图导致 action 失败。
354
+ // 这样图片至少以链接形式存在,用户可手动点击或后续再试。
355
+ console.warn(
356
+ `[AssistantFileResource] remote download failed, keeping original url :: ${describe_error_cause(error instanceof Error ? error : new Error(String(error)))} :: url=${raw_url}`,
357
+ );
358
+ return params.part;
359
+ }
360
+ }
206
361
 
362
+ const source = await read_local_resource(params.projectRoot, raw_url);
207
363
  const media_type =
208
364
  String(params.part.mediaType || source.mediaType || "").trim() ||
209
365
  "application/octet-stream";
@@ -216,16 +372,18 @@ async function materialize_file_part(params: {
216
372
  : source.filename,
217
373
  bytes: source.bytes,
218
374
  });
219
-
220
375
  return {
221
376
  ...params.part,
222
377
  mediaType: media_type,
223
- url: to_resources_url(params.projectRoot, file_path),
378
+ url: toAgentRelativePath({
379
+ projectRoot: params.projectRoot,
380
+ filePath: file_path,
381
+ }),
224
382
  };
225
383
  }
226
384
 
227
385
  /**
228
- * 将 assistant file part 中的资源统一落盘为 `resources://` 相对 URL。
386
+ * 将 assistant file part 中的资源统一落盘为 Agent 根目录相对路径。
229
387
  */
230
388
  export async function materializeAssistantFileParts(
231
389
  params: MaterializeAssistantFilePartsParams,