@downcity/agent 1.1.157 → 1.1.160

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantFileResource.d.ts","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAcrC;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAqLD;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,mCAAmC,GAC1C,OAAO,CAAC,UAAU,EAAE,CAAC,CAYvB"}
1
+ {"version":3,"file":"AssistantFileResource.d.ts","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAcrC;;GAEG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AA8RD;;GAEG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,mCAAmC,GAC1C,OAAO,CAAC,UAAU,EAAE,CAAC,CAYvB"}
@@ -98,21 +98,81 @@ async function write_resource_file(params) {
98
98
  return file_path;
99
99
  }
100
100
  async function read_remote_resource(raw_url) {
101
- const response = await fetch(raw_url);
102
- if (!response.ok) {
103
- throw new Error(`Failed to download assistant file resource: ${response.status}`);
101
+ const result = await fetch_with_retry(raw_url);
102
+ if (!result.ok) {
103
+ throw new Error(`Failed to download assistant file resource: ${result.status} :: url=${raw_url}`);
104
104
  }
105
- const array_buffer = await response.arrayBuffer();
105
+ const array_buffer = await result.arrayBuffer();
106
106
  const bytes = Buffer.from(array_buffer);
107
107
  if (bytes.length === 0) {
108
- throw new Error("Downloaded assistant file resource is empty");
108
+ throw new Error(`Downloaded assistant file resource is empty :: url=${raw_url}`);
109
109
  }
110
110
  return {
111
- mediaType: response.headers.get("content-type")?.split(";")[0]?.trim(),
111
+ mediaType: result.headers.get("content-type")?.split(";")[0]?.trim(),
112
112
  filename: filename_from_url(raw_url),
113
113
  bytes,
114
114
  };
115
115
  }
116
+ /**
117
+ * 带重试的 fetch 远程资源。
118
+ *
119
+ * 关键点(中文)
120
+ * - 对 transient 网络错误(fetch failed / UND_ERR_* / ECONNRESET 等)做 2 次指数退避重试。
121
+ * - 最终失败时把 error.cause 链展开到 message,方便定位是代理问题、DNS 问题还是上游问题。
122
+ */
123
+ async function fetch_with_retry(raw_url) {
124
+ const delays_ms = [250, 1_000];
125
+ const attempts = delays_ms.length + 1;
126
+ let last_error;
127
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
128
+ try {
129
+ return await fetch(raw_url);
130
+ }
131
+ catch (error) {
132
+ last_error = error;
133
+ if (!is_transient_fetch_error(error) || attempt === attempts - 1) {
134
+ throw enrich_fetch_error(error, raw_url);
135
+ }
136
+ const delay_ms = delays_ms[attempt] ?? 1_000;
137
+ await new Promise((resolve) => setTimeout(resolve, delay_ms));
138
+ }
139
+ }
140
+ throw enrich_fetch_error(last_error, raw_url);
141
+ }
142
+ function is_transient_fetch_error(error) {
143
+ if (!(error instanceof Error))
144
+ return false;
145
+ const text = `${error.message} ${describe_error_cause(error)}`;
146
+ return /fetch failed|UND_ERR|ECONN(RESET|REFUSED|ABORTED)|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up/i.test(text);
147
+ }
148
+ function enrich_fetch_error(error, url) {
149
+ if (!(error instanceof Error)) {
150
+ return new Error(`Download failed: ${String(error)} :: url=${url}`);
151
+ }
152
+ const cause_text = describe_error_cause(error);
153
+ const enriched = new Error(`${error.message}${cause_text ? ` :: cause=${cause_text}` : ""} :: url=${url}`, error.cause ? { cause: error.cause } : undefined);
154
+ enriched.stack = error.stack;
155
+ return enriched;
156
+ }
157
+ function describe_error_cause(error) {
158
+ const parts = [];
159
+ let current = error.cause;
160
+ let depth = 0;
161
+ while (current && depth < 3) {
162
+ if (current instanceof Error) {
163
+ const code = current.code;
164
+ const code_text = typeof code === "string" && code ? `[${code}] ` : "";
165
+ parts.push(`${code_text}${current.message || current.name}`.trim());
166
+ current = current.cause;
167
+ }
168
+ else {
169
+ parts.push(String(current));
170
+ break;
171
+ }
172
+ depth += 1;
173
+ }
174
+ return parts.filter(Boolean).join(" -> ");
175
+ }
116
176
  async function read_local_resource(projectRoot, raw_url) {
117
177
  const raw = String(raw_url || "").trim();
118
178
  const file_path = raw.startsWith("file://")
@@ -137,17 +197,48 @@ async function materialize_file_part(params) {
137
197
  if (raw_url.startsWith("resources://"))
138
198
  return params.part;
139
199
  const parsed_data_url = parse_data_url(raw_url);
140
- const source = parsed_data_url
141
- ? {
142
- mediaType: parsed_data_url.media_type,
143
- filename: typeof params.part.filename === "string"
144
- ? params.part.filename
145
- : undefined,
200
+ if (parsed_data_url) {
201
+ const media_type = String(params.part.mediaType || parsed_data_url.media_type || "").trim() ||
202
+ "application/octet-stream";
203
+ const file_path = await write_resource_file({
204
+ projectRoot: params.projectRoot,
205
+ mediaType: media_type,
206
+ filename: typeof params.part.filename === "string" ? params.part.filename : undefined,
146
207
  bytes: parsed_data_url.bytes,
208
+ });
209
+ return {
210
+ ...params.part,
211
+ mediaType: media_type,
212
+ url: to_resources_url(params.projectRoot, file_path),
213
+ };
214
+ }
215
+ if (raw_url.startsWith("http://") || raw_url.startsWith("https://")) {
216
+ try {
217
+ const source = await read_remote_resource(raw_url);
218
+ const media_type = String(params.part.mediaType || source.mediaType || "").trim() ||
219
+ "application/octet-stream";
220
+ const file_path = await write_resource_file({
221
+ projectRoot: params.projectRoot,
222
+ mediaType: media_type,
223
+ filename: typeof params.part.filename === "string"
224
+ ? params.part.filename
225
+ : source.filename,
226
+ bytes: source.bytes,
227
+ });
228
+ return {
229
+ ...params.part,
230
+ mediaType: media_type,
231
+ url: to_resources_url(params.projectRoot, file_path),
232
+ };
147
233
  }
148
- : raw_url.startsWith("http://") || raw_url.startsWith("https://")
149
- ? await read_remote_resource(raw_url)
150
- : await read_local_resource(params.projectRoot, raw_url);
234
+ catch (error) {
235
+ // 关键点(中文):远程下载失败时保留原始 URL,不让整张图导致 action 失败。
236
+ // 这样图片至少以链接形式存在,用户可手动点击或后续再试。
237
+ console.warn(`[AssistantFileResource] remote download failed, keeping original url :: ${describe_error_cause(error instanceof Error ? error : new Error(String(error)))} :: url=${raw_url}`);
238
+ return params.part;
239
+ }
240
+ }
241
+ const source = await read_local_resource(params.projectRoot, raw_url);
151
242
  const media_type = String(params.part.mediaType || source.mediaType || "").trim() ||
152
243
  "application/octet-stream";
153
244
  const file_path = await write_resource_file({
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantFileResource.js","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AssistantFileResource.js","sourceRoot":"","sources":["../../../src/executor/messages/AssistantFileResource.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/agent",
3
- "version": "1.1.157",
3
+ "version": "1.1.160",
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",
@@ -354,3 +354,76 @@ test("ImagePlugin image_create rejects legacy messages and data URLs", async ()
354
354
  assert.equal(data_url_result.success, false);
355
355
  assert.match(data_url_result.error, /does not accept data URLs/);
356
356
  });
357
+
358
+ test("ImagePlugin image_result polls until terminal when until_done=true", async () => {
359
+ const calls = [];
360
+ let next_status = "running";
361
+ const message = create_image_message();
362
+ const plugin = new ImagePlugin({
363
+ image_create: () => ({ job_id: "img_wait", status: "queued", poll_after_ms: 1 }),
364
+ image_result: (input) => {
365
+ calls.push(input.job_id);
366
+ if (calls.length >= 3) {
367
+ next_status = "succeeded";
368
+ }
369
+ if (next_status === "succeeded") {
370
+ return {
371
+ job_id: input.job_id,
372
+ status: "succeeded",
373
+ result: message,
374
+ poll_after_ms: 1,
375
+ };
376
+ }
377
+ return {
378
+ job_id: input.job_id,
379
+ status: "running",
380
+ message: "still going",
381
+ poll_after_ms: 1,
382
+ };
383
+ },
384
+ });
385
+
386
+ const result = await plugin.actions.image_result.execute({
387
+ context: create_context(),
388
+ payload: {
389
+ job_id: "img_wait",
390
+ until_done: true,
391
+ max_wait_ms: 500,
392
+ poll_interval_ms: 5,
393
+ },
394
+ pluginName: "image",
395
+ actionName: "image_result",
396
+ });
397
+
398
+ assert.equal(result.success, true);
399
+ assert.equal(result.data.role, "assistant");
400
+ assert.ok(calls.length >= 3);
401
+ });
402
+
403
+ test("ImagePlugin image_result returns last status when max_wait_ms elapses", async () => {
404
+ const plugin = new ImagePlugin({
405
+ image_create: () => ({ job_id: "img_timeout", status: "queued", poll_after_ms: 1 }),
406
+ image_result: (input) => ({
407
+ job_id: input.job_id,
408
+ status: "running",
409
+ message: "always pending",
410
+ poll_after_ms: 1,
411
+ }),
412
+ });
413
+
414
+ const result = await plugin.actions.image_result.execute({
415
+ context: create_context(),
416
+ payload: {
417
+ job_id: "img_timeout",
418
+ until_done: true,
419
+ max_wait_ms: 30,
420
+ poll_interval_ms: 5,
421
+ },
422
+ pluginName: "image",
423
+ actionName: "image_result",
424
+ });
425
+
426
+ assert.equal(result.success, true);
427
+ assert.equal(result.data.status, "running");
428
+ assert.equal(result.data.message, "always pending");
429
+ });
@@ -140,22 +140,92 @@ async function read_remote_resource(raw_url: string): Promise<{
140
140
  filename?: string;
141
141
  bytes: Buffer;
142
142
  }> {
143
- const response = await fetch(raw_url);
144
- if (!response.ok) {
145
- throw new Error(`Failed to download assistant file resource: ${response.status}`);
143
+ const result = await fetch_with_retry(raw_url);
144
+ if (!result.ok) {
145
+ throw new Error(
146
+ `Failed to download assistant file resource: ${result.status} :: url=${raw_url}`,
147
+ );
146
148
  }
147
- const array_buffer = await response.arrayBuffer();
149
+ const array_buffer = await result.arrayBuffer();
148
150
  const bytes = Buffer.from(array_buffer);
149
151
  if (bytes.length === 0) {
150
- throw new Error("Downloaded assistant file resource is empty");
152
+ throw new Error(`Downloaded assistant file resource is empty :: url=${raw_url}`);
151
153
  }
152
154
  return {
153
- mediaType: response.headers.get("content-type")?.split(";")[0]?.trim(),
155
+ mediaType: result.headers.get("content-type")?.split(";")[0]?.trim(),
154
156
  filename: filename_from_url(raw_url),
155
157
  bytes,
156
158
  };
157
159
  }
158
160
 
161
+ /**
162
+ * 带重试的 fetch 远程资源。
163
+ *
164
+ * 关键点(中文)
165
+ * - 对 transient 网络错误(fetch failed / UND_ERR_* / ECONNRESET 等)做 2 次指数退避重试。
166
+ * - 最终失败时把 error.cause 链展开到 message,方便定位是代理问题、DNS 问题还是上游问题。
167
+ */
168
+ async function fetch_with_retry(
169
+ raw_url: string,
170
+ ): Promise<Response> {
171
+ const delays_ms = [250, 1_000];
172
+ const attempts = delays_ms.length + 1;
173
+ let last_error: unknown;
174
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
175
+ try {
176
+ return await fetch(raw_url);
177
+ } catch (error) {
178
+ last_error = error;
179
+ if (!is_transient_fetch_error(error) || attempt === attempts - 1) {
180
+ throw enrich_fetch_error(error, raw_url);
181
+ }
182
+ const delay_ms = delays_ms[attempt] ?? 1_000;
183
+ await new Promise((resolve) => setTimeout(resolve, delay_ms));
184
+ }
185
+ }
186
+ throw enrich_fetch_error(last_error, raw_url);
187
+ }
188
+
189
+ function is_transient_fetch_error(error: unknown): boolean {
190
+ if (!(error instanceof Error)) return false;
191
+ const text = `${error.message} ${describe_error_cause(error)}`;
192
+ return /fetch failed|UND_ERR|ECONN(RESET|REFUSED|ABORTED)|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|socket hang up/i.test(
193
+ text,
194
+ );
195
+ }
196
+
197
+ function enrich_fetch_error(error: unknown, url: string): Error {
198
+ if (!(error instanceof Error)) {
199
+ return new Error(`Download failed: ${String(error)} :: url=${url}`);
200
+ }
201
+ const cause_text = describe_error_cause(error);
202
+ const enriched = new Error(
203
+ `${error.message}${cause_text ? ` :: cause=${cause_text}` : ""} :: url=${url}`,
204
+ error.cause ? { cause: error.cause } : undefined,
205
+ );
206
+ enriched.stack = error.stack;
207
+ return enriched;
208
+ }
209
+
210
+ function describe_error_cause(error: Error): string {
211
+ const parts: string[] = [];
212
+ let current: unknown = (error as { cause?: unknown }).cause;
213
+ let depth = 0;
214
+ while (current && depth < 3) {
215
+ if (current instanceof Error) {
216
+ const code = (current as { code?: unknown }).code;
217
+ const code_text = typeof code === "string" && code ? `[${code}] ` : "";
218
+ parts.push(`${code_text}${current.message || current.name}`.trim());
219
+ current = (current as { cause?: unknown }).cause;
220
+ } else {
221
+ parts.push(String(current));
222
+ break;
223
+ }
224
+ depth += 1;
225
+ }
226
+ return parts.filter(Boolean).join(" -> ");
227
+ }
228
+
159
229
  async function read_local_resource(
160
230
  projectRoot: string,
161
231
  raw_url: string,
@@ -191,19 +261,55 @@ async function materialize_file_part(params: {
191
261
  if (raw_url.startsWith("resources://")) return params.part;
192
262
 
193
263
  const parsed_data_url = parse_data_url(raw_url);
194
- const source = parsed_data_url
195
- ? {
196
- mediaType: parsed_data_url.media_type,
264
+ if (parsed_data_url) {
265
+ const media_type =
266
+ String(params.part.mediaType || parsed_data_url.media_type || "").trim() ||
267
+ "application/octet-stream";
268
+ const file_path = await write_resource_file({
269
+ projectRoot: params.projectRoot,
270
+ mediaType: media_type,
271
+ filename:
272
+ typeof params.part.filename === "string" ? params.part.filename : undefined,
273
+ bytes: parsed_data_url.bytes,
274
+ });
275
+ return {
276
+ ...params.part,
277
+ mediaType: media_type,
278
+ url: to_resources_url(params.projectRoot, file_path),
279
+ };
280
+ }
281
+
282
+ if (raw_url.startsWith("http://") || raw_url.startsWith("https://")) {
283
+ try {
284
+ const source = await read_remote_resource(raw_url);
285
+ const media_type =
286
+ String(params.part.mediaType || source.mediaType || "").trim() ||
287
+ "application/octet-stream";
288
+ const file_path = await write_resource_file({
289
+ projectRoot: params.projectRoot,
290
+ mediaType: media_type,
197
291
  filename:
198
292
  typeof params.part.filename === "string"
199
293
  ? 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);
294
+ : source.filename,
295
+ bytes: source.bytes,
296
+ });
297
+ return {
298
+ ...params.part,
299
+ mediaType: media_type,
300
+ url: to_resources_url(params.projectRoot, file_path),
301
+ };
302
+ } catch (error) {
303
+ // 关键点(中文):远程下载失败时保留原始 URL,不让整张图导致 action 失败。
304
+ // 这样图片至少以链接形式存在,用户可手动点击或后续再试。
305
+ console.warn(
306
+ `[AssistantFileResource] remote download failed, keeping original url :: ${describe_error_cause(error instanceof Error ? error : new Error(String(error)))} :: url=${raw_url}`,
307
+ );
308
+ return params.part;
309
+ }
310
+ }
206
311
 
312
+ const source = await read_local_resource(params.projectRoot, raw_url);
207
313
  const media_type =
208
314
  String(params.part.mediaType || source.mediaType || "").trim() ||
209
315
  "application/octet-stream";
@@ -216,7 +322,6 @@ async function materialize_file_part(params: {
216
322
  : source.filename,
217
323
  bytes: source.bytes,
218
324
  });
219
-
220
325
  return {
221
326
  ...params.part,
222
327
  mediaType: media_type,