@0xmaxma/claude-gateway 1.5.8 → 1.6.0

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 (65) hide show
  1. package/README.md +22 -0
  2. package/dist/agent/runner.d.ts +30 -0
  3. package/dist/agent/runner.d.ts.map +1 -1
  4. package/dist/agent/runner.js +223 -16
  5. package/dist/agent/runner.js.map +1 -1
  6. package/dist/api/apps-router.d.ts.map +1 -1
  7. package/dist/api/apps-router.js +7 -2
  8. package/dist/api/apps-router.js.map +1 -1
  9. package/dist/api/gateway-router.d.ts.map +1 -1
  10. package/dist/api/gateway-router.js +27 -0
  11. package/dist/api/gateway-router.js.map +1 -1
  12. package/dist/api/line-webhook-router.d.ts.map +1 -1
  13. package/dist/api/line-webhook-router.js +68 -0
  14. package/dist/api/line-webhook-router.js.map +1 -1
  15. package/dist/api/router.d.ts +2 -0
  16. package/dist/api/router.d.ts.map +1 -1
  17. package/dist/api/router.js +59 -3
  18. package/dist/api/router.js.map +1 -1
  19. package/dist/api/share-router.d.ts +11 -0
  20. package/dist/api/share-router.d.ts.map +1 -0
  21. package/dist/api/share-router.js +402 -0
  22. package/dist/api/share-router.js.map +1 -0
  23. package/dist/apps/installer.d.ts +55 -0
  24. package/dist/apps/installer.d.ts.map +1 -1
  25. package/dist/apps/installer.js +130 -0
  26. package/dist/apps/installer.js.map +1 -1
  27. package/dist/config/loader.d.ts.map +1 -1
  28. package/dist/config/loader.js +9 -0
  29. package/dist/config/loader.js.map +1 -1
  30. package/dist/config/public-url.d.ts +11 -0
  31. package/dist/config/public-url.d.ts.map +1 -0
  32. package/dist/config/public-url.js +41 -0
  33. package/dist/config/public-url.js.map +1 -0
  34. package/dist/config/watcher.d.ts.map +1 -1
  35. package/dist/config/watcher.js +1 -0
  36. package/dist/config/watcher.js.map +1 -1
  37. package/dist/history/db.d.ts +14 -0
  38. package/dist/history/db.d.ts.map +1 -1
  39. package/dist/history/db.js +48 -4
  40. package/dist/history/db.js.map +1 -1
  41. package/dist/history/types.d.ts +5 -0
  42. package/dist/history/types.d.ts.map +1 -1
  43. package/dist/session/process.d.ts.map +1 -1
  44. package/dist/session/process.js +17 -1
  45. package/dist/session/process.js.map +1 -1
  46. package/dist/session/store.d.ts.map +1 -1
  47. package/dist/session/store.js +9 -1
  48. package/dist/session/store.js.map +1 -1
  49. package/dist/share/session-image-catalog.d.ts +22 -0
  50. package/dist/share/session-image-catalog.d.ts.map +1 -0
  51. package/dist/share/session-image-catalog.js +151 -0
  52. package/dist/share/session-image-catalog.js.map +1 -0
  53. package/dist/share/share-store.d.ts +137 -0
  54. package/dist/share/share-store.d.ts.map +1 -0
  55. package/dist/share/share-store.js +340 -0
  56. package/dist/share/share-store.js.map +1 -0
  57. package/dist/types.d.ts +17 -7
  58. package/dist/types.d.ts.map +1 -1
  59. package/mcp/instructions.ts +1 -0
  60. package/mcp/server.ts +2 -0
  61. package/mcp/tools/image/module.ts +240 -26
  62. package/mcp/tools/line/module.ts +186 -0
  63. package/mcp/tools/share-image/module.ts +120 -0
  64. package/mcp/tools/shared/share-client.ts +197 -0
  65. package/package.json +3 -1
package/dist/types.d.ts CHANGED
@@ -146,13 +146,15 @@ export interface GatewayConfig {
146
146
  */
147
147
  bind?: string;
148
148
  /**
149
- * Absolute, externally-reachable origin of this gateway (e.g.
150
- * "https://gateway.example.com"), used to build phone-openable links such as
151
- * the `/cli` webview terminal viewer. The process cannot infer its own public
152
- * URL (it binds localhost by default and sits behind a reverse proxy), so the
153
- * operator sets it explicitly. Unset = features that need a public link (e.g.
154
- * `/cli`) are disabled and report that they are not configured. No trailing
155
- * slash required; a trailing slash is trimmed when building links.
149
+ * Absolute, externally-reachable base URL of this gateway, including any
150
+ * reverse-proxy path prefix (e.g. "https://gateway.example.com" or
151
+ * "https://vm.example.com/gateway"). Two consumers: phone-openable links
152
+ * such as the `/cli` webview terminal viewer, and the image-share bridge —
153
+ * its presence is the sole enable switch and source for minting public
154
+ * shared-image URLs. The process cannot infer its own public URL (it binds
155
+ * localhost by default and sits behind a reverse proxy), so the operator
156
+ * sets it explicitly. Unset = features that need a public link are
157
+ * disabled. A trailing slash is trimmed when building links.
156
158
  */
157
159
  publicUrl?: string;
158
160
  models?: ModelConfig[];
@@ -256,6 +258,14 @@ export type ImageParams = {
256
258
  size?: string;
257
259
  aspect_ratio?: string;
258
260
  image_ref?: string;
261
+ /**
262
+ * Reference images explicitly selected in the composer, in the order the user
263
+ * picked them. Each entry is a `ref` value from GET /api/v1/image-catalog —
264
+ * either an `artifact:<id>` ref or a media-relative path (the same forms the
265
+ * generate_image `image`/`images` arguments accept). Per-turn only: unlike the
266
+ * rest of ImageParams these are NOT persisted as durable session image config.
267
+ */
268
+ image_refs?: string[];
259
269
  n?: number;
260
270
  };
261
271
  export type StreamEvent = {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -15,6 +15,7 @@ const IMAGE_INSTRUCTION = [
15
15
  '• RETRY ON FAILURE ONLY: a generate can fail with no_supply / no_credential (pool_eligible does not guarantee a working key). If — AND ONLY IF — a generate returns no_supply, no_credential, or an error, try the NEXT usable model (byok_available ones first), up to every usable model. This retry rule NEVER applies to a successful generate or to sending.',
16
16
  '• ONE image, sent ONCE, then STOP: as soon as a generate SUCCEEDS, deliver that single file with your reply tool exactly ONE time (files: ["/abs/path.png"]) and briefly mention the model. Then you are DONE — do NOT generate again, do NOT resend the same image, do NOT call the reply tool again, and do NOT send extra follow-up messages. Sending the image (or any message) repeatedly is a bug.',
17
17
  '• Only if there is NO usable model at all (none byok_available AND none pool_eligible), OR every usable model failed to generate, tell the user PLAINLY that image generation isn\'t set up with a working model yet and they need to connect an image-provider key (BYOK). Do NOT invent app-install / MCP-setup steps, and do NOT pretend an image was created.',
18
+ '• REFERRING TO AN EARLIER IMAGE: if the user points at an image from earlier in the chat ("image number 2", "the first image", "the one you just made"), call generate_image with action="list_refs" FIRST and use the "ref" of the item they mean — never count images from your own memory of the conversation. Not remembering any images is NOT evidence there are none: your context may have been reset or compacted while the chat history still has them, so NEVER reply "no images attached / please attach" to such a request before list_refs has returned an empty catalog. If it is ambiguous, or the index they gave does not exist in the catalog, ask them which image instead of guessing.',
18
19
  '• NEVER tell the user to install an app or set up an MCP server for images.',
19
20
  ].join('\n');
20
21
 
package/mcp/server.ts CHANGED
@@ -20,6 +20,7 @@ import { SkillsModule } from './tools/skills/module';
20
20
  import { AgentModule } from './tools/agent/module';
21
21
  import { BrowserModule } from './tools/browser/module';
22
22
  import { ImageModule } from './tools/image/module';
23
+ import { ShareImageModule } from './tools/share-image/module';
23
24
  import { AppsModule } from './tools/apps/module';
24
25
  import { ApiModule } from './tools/api/module';
25
26
  import { buildChannelInstructions } from './instructions';
@@ -42,6 +43,7 @@ const modules: AnyModule[] = [
42
43
  new AgentModule(),
43
44
  new BrowserModule(),
44
45
  new ImageModule(),
46
+ new ShareImageModule(),
45
47
  new AppsModule(),
46
48
  new ApiModule(),
47
49
  ];
@@ -4,6 +4,16 @@ import * as path from 'node:path';
4
4
  import * as dns from 'node:dns';
5
5
  import * as net from 'node:net';
6
6
  import type { ToolModule, McpToolDefinition, McpToolResult, ToolVisibility } from '../../types';
7
+ import {
8
+ ShareClientError,
9
+ createShares,
10
+ listSessionImages,
11
+ registerArtifacts,
12
+ revokeSharesBestEffort,
13
+ shareBridgeEnabled,
14
+ type ShareRef,
15
+ type ShareItem,
16
+ } from '../shared/share-client';
7
17
 
8
18
  /**
9
19
  * Image-generation tool module (#184, Track B).
@@ -61,12 +71,9 @@ export class ImageModule implements ToolModule {
61
71
  toolVisibility: ToolVisibility = 'all-configured';
62
72
 
63
73
  isEnabled(): boolean {
64
- // Enabled when BOTH the endpoint and an auth token resolve (from env or the CLI config
65
- // at ~/.claude/settings.json; see baseUrl()/authToken()) and the tool isn't explicitly
66
- // turned off. Requiring the token here means a config that resolves a URL but no token
67
- // disables the tool cleanly, instead of advertising it and then failing every call with a
68
- // 401 on an empty Bearer.
69
- if (!this.baseUrl() || !this.authToken() || process.env.IMAGE_DISABLED === 'true') return false;
74
+ // Enabled when the image endpoint is configured in ~/.claude/settings.json (see
75
+ // baseUrl()) and not explicitly turned off.
76
+ if (!this.baseUrl() || process.env.IMAGE_DISABLED === 'true') return false;
70
77
  // The Bearer proxy_secret rides every call — refuse a cleartext http URL to a
71
78
  // PUBLIC host (that would leak the secret). http to a local/internal host is a
72
79
  // trusted hop (e.g. host.docker.internal in dev) and stays allowed.
@@ -100,9 +107,11 @@ export class ImageModule implements ToolModule {
100
107
  return this.handleStatus(args);
101
108
  case 'list':
102
109
  return this.handleList();
110
+ case 'list_refs':
111
+ return this.handleListRefs();
103
112
  default:
104
113
  return {
105
- content: [{ type: 'text', text: `generate_image: unknown action "${action}" (expected generate | status | list)` }],
114
+ content: [{ type: 'text', text: `generate_image: unknown action "${action}" (expected generate | status | list | list_refs)` }],
106
115
  isError: true,
107
116
  };
108
117
  }
@@ -187,6 +196,53 @@ export class ImageModule implements ToolModule {
187
196
  return { content: [{ type: 'text', text: body || '[]' }] };
188
197
  }
189
198
 
199
+ /**
200
+ * action="list_refs" (#72) — return the deterministic, gateway-computed catalog
201
+ * of every image in this session so the agent never has to count images from its
202
+ * own transcript (which breaks after compaction/resume). Read-only.
203
+ *
204
+ * Gated on the share bridge exactly like reference minting: with the bridge off
205
+ * there is no catalog endpoint to read, so the action stays inert (legacy mode
206
+ * gains no new behavior).
207
+ */
208
+ private async handleListRefs(): Promise<McpToolResult> {
209
+ if (!shareBridgeEnabled()) {
210
+ return {
211
+ content: [{ type: 'text', text: 'generate_image: list_refs is unavailable (image share bridge is not configured).' }],
212
+ isError: true,
213
+ };
214
+ }
215
+ let items;
216
+ try {
217
+ items = await listSessionImages();
218
+ } catch (err) {
219
+ if (err instanceof ShareClientError) {
220
+ return { content: [{ type: 'text', text: `generate_image: ${err.code}: ${err.message}` }], isError: true };
221
+ }
222
+ return {
223
+ content: [{ type: 'text', text: `generate_image: image share service unavailable: ${(err as Error).message}` }],
224
+ isError: true,
225
+ };
226
+ }
227
+ return {
228
+ content: [{
229
+ type: 'text',
230
+ text: JSON.stringify({
231
+ images: items,
232
+ note: 'Ground-truth catalog of every image in this session, numbered in order of first appearance ("image 1" = index 1). '
233
+ + 'To use one as a reference, pass its "ref" value in the "image"/"images" argument of action="generate". '
234
+ + 'Do NOT count images from conversation memory. '
235
+ + 'Resolution precedence: (1) when the user names an index ("Image 3", "the third image") or attached an image this turn, '
236
+ + 'trust that EXACTLY — never let "desc" override an explicit reference; '
237
+ + '(2) when the user refers by content ("the dog picture"), match against each item\'s "desc" '
238
+ + '(the generation prompt, or the user text that came with an upload); '
239
+ + '(3) if the reference is ambiguous or the index does not exist, ask the user instead of guessing. '
240
+ + 'Items with available:false can no longer be used.',
241
+ }),
242
+ }],
243
+ };
244
+ }
245
+
190
246
  private async handleGenerate(args: Record<string, unknown>): Promise<McpToolResult> {
191
247
  const prompt = typeof args.prompt === 'string' ? args.prompt.trim() : '';
192
248
  const model = typeof args.model === 'string' ? args.model.trim() : '';
@@ -202,13 +258,39 @@ export class ImageModule implements ToolModule {
202
258
  for (const k of ['quality', 'size', 'aspect_ratio', 'style'] as const) {
203
259
  if (typeof args[k] === 'string' && (args[k] as string).length) reqBody[k] = args[k];
204
260
  }
205
- if (typeof args.n === 'number' && args.n > 0) reqBody.n = args.n;
206
- if (typeof args.image === 'string' && args.image.length) reqBody.image = args.image;
207
- if (Array.isArray(args.images) && args.images.every((x) => typeof x === 'string') && args.images.length) {
208
- reqBody.images = args.images;
261
+ if (typeof args.n === 'number' && args.n > 0) {
262
+ // Clamp the requested image count — an unbounded n is forwarded straight to
263
+ // the provider (cost / abuse). MAX_DELIVER_IMAGES is the most we can deliver
264
+ // back anyway, so generating more is wasted spend.
265
+ reqBody.n = Math.min(Math.floor(args.n), MAX_DELIVER_IMAGES);
209
266
  }
210
267
 
211
- // Submit (E1)
268
+ // Reference images (#70): when the share bridge is enabled, local paths and
269
+ // artifact:<id> refs are auto-converted to short-lived public share URLs via
270
+ // the authenticated gateway API (plan §7). With the bridge off, behavior is
271
+ // EXACTLY the legacy pass-through (regression guarantee).
272
+ const rawImage = typeof args.image === 'string' && args.image.length ? args.image : undefined;
273
+ const rawImages =
274
+ Array.isArray(args.images) && args.images.every((x) => typeof x === 'string') && args.images.length
275
+ ? (args.images as string[])
276
+ : undefined;
277
+ let mintedShareIds: string[] = [];
278
+ if (shareBridgeEnabled() && (rawImage || rawImages)) {
279
+ if (rawImage && rawImages) {
280
+ return { content: [{ type: 'text', text: 'generate_image: pass either "image" or "images", not both.' }], isError: true };
281
+ }
282
+ const normalized = await this.normalizeRefs(rawImage ? [rawImage] : rawImages!);
283
+ if ('error' in normalized) return normalized.error;
284
+ mintedShareIds = normalized.mintedShareIds;
285
+ if (rawImage) reqBody.image = normalized.urls[0];
286
+ else reqBody.images = normalized.urls;
287
+ } else {
288
+ if (rawImage) reqBody.image = rawImage;
289
+ if (rawImages) reqBody.images = rawImages;
290
+ }
291
+
292
+ // Submit (E1). On an immediate submit failure, best-effort revoke any share
293
+ // URLs minted for this call (§18) — the TTL still bounds exposure otherwise.
212
294
  let res: Response;
213
295
  try {
214
296
  res = await fetch(`${this.baseUrl()}/v1/images/generations`, {
@@ -218,19 +300,25 @@ export class ImageModule implements ToolModule {
218
300
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
219
301
  });
220
302
  } catch (err) {
303
+ await revokeSharesBestEffort(mintedShareIds);
221
304
  return this.unavailable(err);
222
305
  }
223
306
  const submitText = await res.text().catch(() => '');
224
- if (!res.ok) return this.mapHttpError(res.status, submitText);
307
+ if (!res.ok) {
308
+ await revokeSharesBestEffort(mintedShareIds);
309
+ return this.mapHttpError(res.status, submitText);
310
+ }
225
311
 
226
312
  let submit: JobResponse;
227
313
  try {
228
314
  submit = JSON.parse(submitText) as JobResponse;
229
315
  } catch {
316
+ await revokeSharesBestEffort(mintedShareIds);
230
317
  return { content: [{ type: 'text', text: 'generate_image: invalid JSON from image service on submit' }], isError: true };
231
318
  }
232
319
  const taskId = submit.task_id;
233
320
  if (!taskId) {
321
+ await revokeSharesBestEffort(mintedShareIds);
234
322
  return { content: [{ type: 'text', text: 'generate_image: image service did not return a task_id' }], isError: true };
235
323
  }
236
324
 
@@ -246,7 +334,7 @@ export class ImageModule implements ToolModule {
246
334
  }
247
335
  if (polled.httpError) return this.mapHttpError(polled.httpError.status, polled.httpError.body);
248
336
  last = polled.job!;
249
- if (last.status === 'done') return await this.deliver(last, taskId);
337
+ if (last.status === 'done') return await this.deliver(last, taskId, model, prompt);
250
338
  if (last.status === 'failed') return this.mapJobError(last);
251
339
  }
252
340
 
@@ -287,6 +375,82 @@ export class ImageModule implements ToolModule {
287
375
 
288
376
  // ── helpers ─────────────────────────────────────────────────────────────
289
377
 
378
+ /**
379
+ * Normalize reference inputs (#70, plan §7), preserving order:
380
+ * https URL → validate syntax, pass through
381
+ * http URL → reject (phase 1 requires HTTPS)
382
+ * artifact:<id> → resolve+mint via gateway share API
383
+ * local media path → validate+mint via gateway share API
384
+ * Rejects duplicates and over-count BEFORE any minting/billing.
385
+ */
386
+ private async normalizeRefs(
387
+ refs: string[],
388
+ ): Promise<{ urls: string[]; mintedShareIds: string[] } | { error: McpToolResult }> {
389
+ const fail = (text: string): { error: McpToolResult } => ({
390
+ error: { content: [{ type: 'text', text }], isError: true },
391
+ });
392
+ const maxRefs = (() => {
393
+ const n = Number(process.env.IMAGE_SHARE_MAX_REFS);
394
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : 5;
395
+ })();
396
+ if (refs.length > maxRefs) {
397
+ return fail(`generate_image: too many reference images (max ${maxRefs}).`);
398
+ }
399
+ if (new Set(refs).size !== refs.length) {
400
+ return fail('generate_image: duplicate reference images are not allowed.');
401
+ }
402
+ type Entry = { kind: 'url'; url: string } | { kind: 'local'; ref: ShareRef };
403
+ const entries: Entry[] = [];
404
+ for (const raw of refs) {
405
+ const ref = raw.trim();
406
+ if (/^https:\/\//i.test(ref)) {
407
+ try {
408
+ new URL(ref);
409
+ } catch {
410
+ return fail(`generate_image: reference URL is malformed.`);
411
+ }
412
+ entries.push({ kind: 'url', url: ref });
413
+ } else if (/^http:\/\//i.test(ref)) {
414
+ return fail('generate_image: http:// reference URLs are not allowed — use https.');
415
+ } else if (ref.startsWith('artifact:')) {
416
+ const id = ref.slice('artifact:'.length).trim();
417
+ if (!id) return fail('generate_image: empty artifact reference.');
418
+ entries.push({ kind: 'local', ref: { artifact_id: id } });
419
+ } else {
420
+ entries.push({ kind: 'local', ref: { path: ref } });
421
+ }
422
+ }
423
+ const localRefs = entries.filter((e): e is Extract<Entry, { kind: 'local' }> => e.kind === 'local');
424
+ let minted: ShareItem[] = [];
425
+ if (localRefs.length) {
426
+ try {
427
+ minted = await createShares(localRefs.map((e) => e.ref), { purpose: 'codex_ref' });
428
+ } catch (err) {
429
+ if (err instanceof ShareClientError) {
430
+ if (err.code === 'image_ref_not_found') {
431
+ return fail('generate_image: image_ref_not_found: the referenced image/artifact does not exist in this session.');
432
+ }
433
+ return fail(`generate_image: ${err.code}: ${err.message}`);
434
+ }
435
+ return fail(`generate_image: image share service unavailable: ${(err as Error).message}`);
436
+ }
437
+ if (minted.length !== localRefs.length) {
438
+ await revokeSharesBestEffort(minted.map((m) => m.share_id));
439
+ return fail('generate_image: image share service returned a mismatched share count.');
440
+ }
441
+ // Provider fetches these refs over HTTPS, so each needs an absolute URL —
442
+ // which the share API only fills when gateway.publicUrl is configured.
443
+ if (minted.some((m) => !m.url)) {
444
+ await revokeSharesBestEffort(minted.map((m) => m.share_id));
445
+ return fail('generate_image: image reference sharing requires gateway.publicUrl to be configured.');
446
+ }
447
+ }
448
+ // Merge back preserving the caller's order.
449
+ let next = 0;
450
+ const urls = entries.map((e) => (e.kind === 'url' ? e.url : minted[next++]!.url!));
451
+ return { urls, mintedShareIds: minted.map((m) => m.share_id) };
452
+ }
453
+
290
454
  private pollTimeoutMs(): number {
291
455
  const raw = Number(process.env.IMAGE_POLL_TIMEOUT_MS);
292
456
  return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_POLL_TIMEOUT_MS;
@@ -317,7 +481,10 @@ export class ImageModule implements ToolModule {
317
481
  * item is either base64 bytes (sync providers like openai/gemini) OR an https URL
318
482
  * (async providers like nanobanana/bfl/fal, which return a hosted file) — download
319
483
  * URLs, decode base64. */
320
- private async deliver(job: JobResponse, taskId: string): Promise<McpToolResult> {
484
+ // `prompt` is only known on the generate path (same-turn poll); the
485
+ // action="status" path re-enters in a later turn without it, so those
486
+ // artifacts register promptless.
487
+ private async deliver(job: JobResponse, taskId: string, model?: string, prompt?: string): Promise<McpToolResult> {
321
488
  const allImages = Array.isArray(job.images) ? job.images.filter((s) => typeof s === 'string' && s.length) : [];
322
489
  // Cap how many we process — downloads are sequential (~30s each), so an
323
490
  // over-long list would hang the tool call far past the poll budget.
@@ -338,9 +505,18 @@ export class ImageModule implements ToolModule {
338
505
  let buf: Buffer;
339
506
  if (/^https?:\/\//i.test(item)) {
340
507
  // Async providers (nanobanana / bfl / fal / runway) return a hosted image
341
- // URL — download the bytes. SSRF guard first: https-only + block any host
342
- // that resolves to a private/loopback/link-local/metadata address, and
343
- // redirect:'error' so a later hop can't bounce to an internal target.
508
+ // URL — download the bytes. SSRF guard: https-only + reject any host that
509
+ // resolves to a private/loopback/link-local/metadata address, and
510
+ // redirect:'error' so a 3xx hop can't bounce to an internal target.
511
+ //
512
+ // Residual risk (accepted): this is a best-effort screen, NOT a full
513
+ // rebinding defence. assertSafeImageUrl resolves DNS once; fetch()
514
+ // resolves again (Node/undici does not pin the vetted IP), so a
515
+ // low-TTL record could answer public here and 127.0.0.1 at fetch time.
516
+ // redirect:'error' does NOT close this — it only blocks HTTP redirects,
517
+ // not DNS rebinding. The backstop is downstream: the response body must
518
+ // pass detectImageExt() below, so an internal non-image endpoint yields
519
+ // unrecognized bytes and is rejected before anything is written to disk.
344
520
  await assertSafeImageUrl(item);
345
521
  const res = await fetch(item, { signal: AbortSignal.timeout(30_000), redirect: 'error' });
346
522
  if (!res.ok) throw new Error(`download image failed: HTTP ${res.status}`);
@@ -369,6 +545,28 @@ export class ImageModule implements ToolModule {
369
545
  } catch (err) {
370
546
  return { content: [{ type: 'text', text: `generate_image: failed to save image: ${(err as Error).message}` }], isError: true };
371
547
  }
548
+
549
+ // Register the written files as private artifacts (#70, plan §8) so a later
550
+ // turn can reference them as artifact:<id> for editing. Best-effort: when
551
+ // the bridge is off or registration fails, the response simply carries no
552
+ // artifacts — file delivery is unaffected.
553
+ let artifacts: Array<Record<string, unknown>> | undefined;
554
+ if (shareBridgeEnabled() && files.length) {
555
+ const slash = (model ?? '').indexOf('/');
556
+ const provider = slash > 0 ? (model as string).slice(0, slash) : 'unknown';
557
+ const modelName = slash > 0 ? (model as string).slice(slash + 1) : (model || 'unknown');
558
+ const registered = await registerArtifacts(files, { provider, model: modelName, taskId, prompt });
559
+ if (registered) {
560
+ artifacts = registered.map((item, index) => ({
561
+ artifact_id: item.artifact_id,
562
+ artifact_ref: item.artifact_ref,
563
+ index,
564
+ path: files[index],
565
+ provider,
566
+ model: modelName,
567
+ }));
568
+ }
569
+ }
372
570
  return {
373
571
  content: [{
374
572
  type: 'text',
@@ -378,8 +576,10 @@ export class ImageModule implements ToolModule {
378
576
  byok: job.byok ?? false,
379
577
  cost: job.cost ?? 0,
380
578
  files,
579
+ ...(artifacts ? { artifacts } : {}),
381
580
  ...(droppedImages > 0 ? { dropped_images: droppedImages } : {}),
382
- note: 'Image saved. Deliver it to the user with your channel reply tool (files: [...]) — e.g. api_reply, reply.'
581
+ note: 'Image saved. Deliver it to the user with your channel delivery tool — api_reply/reply (files: [...]), or line_image on LINE. Do NOT open/Read the file to inspect it first; attach it and answer briefly.'
582
+ + (artifacts ? ' To edit this image later, reference it via its artifact_ref (e.g. image: "artifact:...").' : '')
383
583
  + (droppedImages > 0 ? ` (${droppedImages} extra image(s) beyond the cap were not saved)` : ''),
384
584
  }),
385
585
  }],
@@ -450,8 +650,10 @@ function sanitize(s: string): string {
450
650
  // SSRF guard for provider image URLs. Require https, then resolve the host and
451
651
  // reject if ANY resolved address is private / loopback / link-local / metadata —
452
652
  // so a compromised provider response can't make the gateway fetch internal or
453
- // cloud-metadata endpoints. Best-effort screen (a DNS rebind between this lookup
454
- // and the fetch is still bounded by redirect:'error' at the call site).
653
+ // cloud-metadata endpoints. Best-effort screen only: a DNS rebind between this
654
+ // lookup and the fetch is NOT prevented here (Node does not let us pin the vetted
655
+ // IP without a custom undici dispatcher). The real backstop is the caller's
656
+ // post-download detectImageExt() check — non-image bytes are rejected before use.
455
657
  async function assertSafeImageUrl(raw: string): Promise<void> {
456
658
  let u: URL;
457
659
  try {
@@ -592,7 +794,10 @@ const imageToolDefs: McpToolDefinition[] = [
592
794
  'Use this WHENEVER the user asks to create, draw, make, or edit an image — it is built in, no app install needed. ' +
593
795
  'Generate images from a text prompt (optionally guided by a reference image) via the configured image generation service. ' +
594
796
  'action="generate" submits the request and returns the saved image file path(s) once ready — ' +
595
- 'then deliver them with your channel reply tool (files: [...]). ' +
797
+ 'then deliver them with your channel reply tool (files: [...]). AFTER A SUCCESSFUL GENERATE: do NOT ' +
798
+ 'open/Read the produced file to inspect it and do NOT re-analyze it — the generation already succeeded; ' +
799
+ 'attach it with your reply tool and answer in one or two short sentences. Every extra step risks pushing ' +
800
+ 'the request past its timeout. ' +
596
801
  'action="status" polls a previously returned task_id. ' +
597
802
  'action="list" returns every available image model with its supported_qualities, supported_sizes, cost, and ' +
598
803
  'the capability flags supports_image_ref (image-to-image / edit) and supports_style_ref. Call it FIRST when ' +
@@ -603,14 +808,23 @@ const imageToolDefs: McpToolDefinition[] = [
603
808
  'img2img-capable model is available — or the model you picked has supports_image_ref=false — do NOT pass ' +
604
809
  '"image": instead look at the reference image yourself, describe what matters in the "prompt", and generate ' +
605
810
  'text-to-image. Never send "image" to a model that does not support it. ' +
811
+ 'EARLIER IMAGES IN THE CHAT: when the user points at an image from earlier in this conversation ' +
812
+ '("image number 2", "the first image", "the picture you just made", "that logo from before"), call ' +
813
+ 'action="list_refs" FIRST. It returns the ground-truth catalog of this session\'s images numbered by ' +
814
+ 'first appearance; pass the chosen item\'s "ref" into "image"/"images". NEVER work out which earlier ' +
815
+ 'image the user means by counting from your own memory of the conversation — the numbering there is ' +
816
+ 'not reliable, and not remembering any images is NOT evidence there are none (your context may have ' +
817
+ 'been reset while the chat history still has them): never answer "no images attached" to such a ' +
818
+ 'request before list_refs has returned an empty catalog. If the catalog makes the request ambiguous ' +
819
+ '(or the requested index does not exist), ask the user which image they mean instead of guessing. ' +
606
820
  'When the user selected options in the composer (an <image-params .../> tag in the turn), honor those values.',
607
821
  inputSchema: {
608
822
  type: 'object',
609
823
  properties: {
610
824
  action: {
611
825
  type: 'string',
612
- enum: ['generate', 'status', 'list'],
613
- description: 'generate (default) | status | list',
826
+ enum: ['generate', 'status', 'list', 'list_refs'],
827
+ description: 'generate (default) | status | list | list_refs (numbered catalog of this session\'s images, for resolving "the second image" style references)',
614
828
  },
615
829
  model: {
616
830
  type: 'string',
@@ -621,8 +835,8 @@ const imageToolDefs: McpToolDefinition[] = [
621
835
  size: { type: 'string', description: 'Optional size, e.g. "1024x1024".' },
622
836
  aspect_ratio: { type: 'string', description: 'Optional aspect ratio, e.g. "1:1" (converted to size if the provider needs it).' },
623
837
  n: { type: 'integer', description: 'Optional number of images (default 1).' },
624
- image: { type: 'string', description: 'Optional reference-image media path for image-to-image/edit (e.g. "media/xxx.png"). ONLY pass this to a model whose supports_image_ref is true (check action="list"); for any other model, describe the reference image in the prompt instead of sending it here.' },
625
- images: { type: 'array', items: { type: 'string' }, description: 'Optional multiple reference-image media paths (same supports_image_ref rule as "image").' },
838
+ image: { type: 'string', description: 'Optional reference image for image-to-image/edit: a media path (e.g. "media/xxx.png"), an "artifact:<id>" ref from a previous generate_image result, or an https URL. Local/artifact refs are converted to short-lived URLs automatically. ONLY pass this to a model whose supports_image_ref is true (check action="list"); for any other model, describe the reference image in the prompt instead of sending it here. Do not pass both "image" and "images".' },
839
+ images: { type: 'array', items: { type: 'string' }, description: 'Optional multiple reference images (max 5; media paths, artifact:<id> refs, or https URLs — same supports_image_ref rule as "image"). Order is preserved.' },
626
840
  style: { type: 'string', description: 'Optional native style parameter (e.g. "vivid") — only for models whose supports_style_ref is true.' },
627
841
  task_id: { type: 'string', description: 'Job id to poll (required for action="status").' },
628
842
  },