ima2-gen 2.0.1 → 2.0.2

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 (63) hide show
  1. package/CHANGELOG.md +150 -0
  2. package/README.md +10 -1
  3. package/bin/commands/backfillThumbs.js +6 -0
  4. package/bin/commands/gen.js +6 -0
  5. package/bin/ima2.js +14 -10
  6. package/docs/API.md +131 -8
  7. package/docs/CLI.md +2 -1
  8. package/docs/FAQ.ko.md +16 -0
  9. package/docs/FAQ.md +30 -0
  10. package/docs/README.ko.md +7 -3
  11. package/docs/migration/runtime-test-inventory.md +15 -1
  12. package/lib/agentImageVideoGen.js +261 -0
  13. package/lib/agentRuntime.js +7 -262
  14. package/lib/agyImageAdapter.js +35 -8
  15. package/lib/errorClassify.js +8 -7
  16. package/lib/eventBus.js +71 -0
  17. package/lib/geminiApiImageAdapter.js +16 -20
  18. package/lib/generationErrors.js +3 -1
  19. package/lib/grokImageAdapter.js +68 -129
  20. package/lib/grokImageCore.js +153 -0
  21. package/lib/grokMultimodeAdapter.js +5 -3
  22. package/lib/grokVideoCanvas.js +13 -0
  23. package/lib/grokVideoPlannerPrompt.js +53 -6
  24. package/lib/historyList.js +1 -0
  25. package/lib/inflight.js +54 -17
  26. package/lib/multimodeHelpers.js +10 -0
  27. package/lib/nodeHelpers.js +59 -0
  28. package/lib/oauthProxy/prompts.js +30 -36
  29. package/lib/promptBuilder/systemPrompt.js +2 -5
  30. package/lib/promptSafetyPolicy.js +1 -5
  31. package/lib/responsesFallback.js +2 -1
  32. package/lib/routeHelpers.js +44 -0
  33. package/lib/ssePublish.js +12 -0
  34. package/lib/storyboardPrefix.js +28 -0
  35. package/lib/thumbBackfill.js +16 -5
  36. package/package.json +4 -1
  37. package/routes/agy.js +44 -0
  38. package/routes/auth.js +6 -2
  39. package/routes/edit.js +7 -1
  40. package/routes/events.js +78 -0
  41. package/routes/generate.js +99 -127
  42. package/routes/index.js +4 -0
  43. package/routes/multimode.js +99 -56
  44. package/routes/nodes.js +59 -103
  45. package/routes/video.js +100 -17
  46. package/skills/ima2/SKILL.md +98 -21
  47. package/ui/dist/.vite/manifest.json +12 -12
  48. package/ui/dist/assets/{AgentWorkspace-CYv84Rus.js → AgentWorkspace-Dth6YijN.js} +1 -1
  49. package/ui/dist/assets/{CardNewsWorkspace-Dqyc1WZ1.js → CardNewsWorkspace-Dav3K5CT.js} +1 -1
  50. package/ui/dist/assets/{NodeCanvas-ChEXzQbb.js → NodeCanvas-C4ifFzB1.js} +1 -1
  51. package/ui/dist/assets/{PromptBuilderPanel-B95ZufnR.js → PromptBuilderPanel-CEcyU9PL.js} +1 -1
  52. package/ui/dist/assets/{PromptImportDialog-DGOwFQET.js → PromptImportDialog-CgQ94Gth.js} +2 -2
  53. package/ui/dist/assets/{PromptImportDiscoverySection-CgvdnR49.js → PromptImportDiscoverySection-CuzyzbNI.js} +1 -1
  54. package/ui/dist/assets/{PromptImportFolderSection-CfUye9J8.js → PromptImportFolderSection-DHLGlO6l.js} +1 -1
  55. package/ui/dist/assets/{PromptLibraryPanel-B9kndPw1.js → PromptLibraryPanel-BOe18we8.js} +2 -2
  56. package/ui/dist/assets/SettingsWorkspace-Cdgnm4Wa.js +1 -0
  57. package/ui/dist/assets/{index-BhcvL0g-.js → index-C5PSahkr.js} +1 -1
  58. package/ui/dist/assets/index-Dn2AhL6d.css +1 -0
  59. package/ui/dist/assets/index-Tjqx6wUV.js +23 -0
  60. package/ui/dist/index.html +2 -2
  61. package/ui/dist/assets/SettingsWorkspace-B3tgLrmF.js +0 -1
  62. package/ui/dist/assets/index-BtK3YhJc.js +0 -39
  63. package/ui/dist/assets/index-ClOLOjnA.css +0 -1
@@ -12,7 +12,7 @@ import { generateViaGrok, planGrokImage } from "../lib/grokImageAdapter.js";
12
12
  import { generateViaAgy } from "../lib/agyImageAdapter.js";
13
13
  import { generateViaGeminiApi } from "../lib/geminiApiImageAdapter.js";
14
14
  import { isNonRetryableGenerationError, normalizeGenerationFailure } from "../lib/generationErrors.js";
15
- import { startJob, finishJob, registerJobAbortController, isJobCanceled } from "../lib/inflight.js";
15
+ import { startJob, finishJob, registerJobAbortController, isJobCanceled, isStartJobFailure, setJobPhase, INFLIGHT_RETRY_AFTER_SECONDS, } from "../lib/inflight.js";
16
16
  import { isGenerationCanceledError, makeGenerationCanceledError, throwIfJobCanceled, } from "../lib/generationCancel.js";
17
17
  import { logEvent, logError } from "../lib/logger.js";
18
18
  import { embedImageMetadataBestEffort } from "../lib/imageMetadataStore.js";
@@ -20,57 +20,44 @@ import { invalidateHistoryIndex } from "../lib/historyIndex.js";
20
20
  import { normalizeComposerInsertedPrompts, normalizeComposerPrompt, } from "../lib/composerSnapshot.js";
21
21
  import { errInfo } from "../lib/errInfo.js";
22
22
  import { requireRuntimeContext } from "../lib/runtimeContext.js";
23
- function validateModeration(ctx, moderation) {
24
- if (typeof moderation !== "string" || !ctx.config.oauth.validModeration.has(moderation)) {
25
- return { error: "moderation must be one of: auto, low" };
26
- }
27
- return { moderation };
28
- }
29
- function imageFormatFromMime(mime) {
30
- if (mime === "image/jpeg")
31
- return "jpeg";
32
- if (mime === "image/webp")
33
- return "webp";
34
- return "png";
35
- }
23
+ import { STORYBOARD_PREFIX } from "../lib/storyboardPrefix.js";
24
+ import { validateModeration, imageFormatFromMime, upstreamErrorFields } from "../lib/routeHelpers.js";
25
+ import { publish } from "../lib/eventBus.js";
26
+ import { publishJobEvent } from "../lib/ssePublish.js";
36
27
  export function registerGenerateRoutes(app, ctxRaw) {
37
28
  const ctx = requireRuntimeContext(ctxRaw);
38
29
  app.post("/api/generate", async (req, res) => {
39
30
  const requestId = typeof req.body?.requestId === "string" ? req.body.requestId : req.id;
31
+ const asyncMode = req.body?.async === true;
40
32
  let finishStatus = "completed";
41
33
  let finishHttpStatus;
42
34
  let finishErrorCode;
43
35
  let finishMeta = {};
44
36
  let finishCanceled = false;
45
37
  const cancelController = new AbortController();
38
+ const fail = (status, payload) => {
39
+ finishStatus = "error";
40
+ finishHttpStatus = status;
41
+ finishErrorCode = typeof payload.code === "string" ? payload.code : finishErrorCode;
42
+ if (asyncMode && res.headersSent) {
43
+ publish(requestId, "error", { ...payload, status, requestId });
44
+ return;
45
+ }
46
+ return res.status(status).json(payload);
47
+ };
48
+ const succeed = (payload) => {
49
+ if (asyncMode) {
50
+ publishJobEvent(requestId, "done", payload);
51
+ return;
52
+ }
53
+ res.json(payload);
54
+ };
46
55
  try {
47
56
  const sessionId = typeof req.body?.sessionId === "string" ? req.body.sessionId : null;
48
57
  const clientNodeId = typeof req.body?.clientNodeId === "string" ? req.body.clientNodeId : null;
49
58
  const { prompt, quality: rawQuality = "medium", size = "1024x1024", format = "png", moderation = "low", provider = "auto", n = 1, references = [], mode: promptMode = "auto", model: rawModel, reasoningEffort: rawReasoningEffort, webSearchEnabled: rawWebSearchEnabled = true, } = req.body;
50
59
  const storyboardActive = req.body?.storyboard === true;
51
- const storyboardPrefix = storyboardActive
52
- ? [
53
- "[STORYBOARD MODE — Video Production Keyframe]",
54
- "This image is a keyframe for a multi-shot VIDEO storyboard. It will be animated via image-to-video.",
55
- "The prompt and all injected instructions MUST be in English.",
56
- "",
57
- "CHARACTER LOCK:",
58
- "- Identify each character by 2-3 VISUAL identifiers (clothing color + physique + position/props). Never by name alone.",
59
- "- Copy character descriptions VERBATIM from the reference/prior frame. Do NOT rephrase or drift.",
60
- "",
61
- "SCENE CONTINUITY:",
62
- "- Lock lighting direction, color palette, environment, and art style to prior frames.",
63
- "- Change ONLY: action, shot scale, camera angle, or expression.",
64
- "- Reference image = canonical anchor. Preserve it faithfully.",
65
- "",
66
- "VIDEO-READY COMPOSITION:",
67
- "- Frame for animation: leave space for motion, avoid static-only poses.",
68
- "- Use descriptive caption format: shot type + subject action + environment + technical (lens, lighting) + mood.",
69
- "- Specify intended camera movement for the video phase (e.g. 'slow dolly-in', 'static wide').",
70
- "- End pose must be stable and suitable for video continuation.",
71
- "",
72
- ].join("\n") + "\n"
73
- : "";
60
+ const storyboardPrefix = storyboardActive ? STORYBOARD_PREFIX : "";
74
61
  const composerPrompt = normalizeComposerPrompt(req.body?.composerPrompt);
75
62
  const composerInsertedPrompts = normalizeComposerInsertedPrompts(req.body?.composerInsertedPrompts);
76
63
  const { quality, warnings: qualityWarnings } = normalizeOAuthParams({ provider, quality: rawQuality });
@@ -82,10 +69,7 @@ export function registerGenerateRoutes(app, ctxRaw) {
82
69
  rawWebSearchEnabled,
83
70
  });
84
71
  if (providerOptions.error) {
85
- finishStatus = "error";
86
- finishHttpStatus = providerOptions.status;
87
- finishErrorCode = providerOptions.code;
88
- return res.status(providerOptions.status).json({ error: providerOptions.error, code: providerOptions.code });
72
+ return fail(providerOptions.status, { error: providerOptions.error, code: providerOptions.code });
89
73
  }
90
74
  const imageModel = providerOptions.model;
91
75
  const reasoningEffort = providerOptions.reasoningEffort;
@@ -95,13 +79,34 @@ export function registerGenerateRoutes(app, ctxRaw) {
95
79
  const normalizedPromptMode = promptMode === "direct" ? "direct" : "auto";
96
80
  const generationPrompt = storyboardPrefix + prompt;
97
81
  if (!prompt)
98
- return res.status(400).json({ error: "Prompt is required" });
82
+ return fail(400, { error: "Prompt is required" });
99
83
  const moderationCheck = validateModeration(ctx, moderation);
100
84
  if (moderationCheck.error)
101
- return res.status(400).json({ error: moderationCheck.error });
85
+ return fail(400, { error: moderationCheck.error });
102
86
  const count = Math.min(Math.max(parseInt(n) || 1, 1), 8);
103
87
  const referencePayload = summarizeReferencePayload(references);
104
- startJob({
88
+ const refCheckResult = validateAndNormalizeRefs(references);
89
+ if (refCheckResult.error) {
90
+ return fail(400, { error: refCheckResult.error, code: refCheckResult.code });
91
+ }
92
+ const refCheck = refCheckResult;
93
+ const incomingProviderUrl = typeof req.body?.providerUrl === "string" && req.body.providerUrl.startsWith("http")
94
+ ? req.body.providerUrl
95
+ : null;
96
+ const grokRefs = incomingProviderUrl
97
+ ? [{ b64: "", url: incomingProviderUrl, declaredMime: "image/png", detectedMime: "image/png" }, ...refCheck.refDetails]
98
+ : refCheck.refDetails;
99
+ const providerRefCount = activeProvider === "grok" || activeProvider === "grok-api"
100
+ ? grokRefs.length
101
+ : refCheck.refs.length;
102
+ if ((activeProvider === "grok" || activeProvider === "agy" || activeProvider === "grok-api" || activeProvider === "gemini-api") && providerRefCount > 3) {
103
+ return fail(400, {
104
+ error: `${activeProvider === "agy" ? "Agy" : "Grok"} image editing supports up to 3 reference images`,
105
+ code: activeProvider === "agy" ? "AGY_REF_TOO_MANY" : "GROK_REF_TOO_MANY",
106
+ requestId,
107
+ });
108
+ }
109
+ const started = startJob({
105
110
  requestId,
106
111
  kind: "classic",
107
112
  prompt,
@@ -114,32 +119,33 @@ export function registerGenerateRoutes(app, ctxRaw) {
114
119
  model: imageModel,
115
120
  size: effectiveSize,
116
121
  n: count,
117
- refsCount: referencePayload.refsCount,
122
+ refsCount: providerRefCount,
118
123
  referenceBytes: referencePayload.referenceBytes,
119
124
  referenceB64Chars: referencePayload.referenceB64Chars,
120
125
  composerPrompt,
121
126
  composerInsertedPrompts,
122
127
  },
123
128
  });
124
- registerJobAbortController(requestId, cancelController);
125
- const refCheckResult = validateAndNormalizeRefs(references);
126
- if (refCheckResult.error) {
127
- finishStatus = "error";
128
- finishHttpStatus = 400;
129
- finishErrorCode = refCheckResult.code;
130
- return res.status(400).json({ error: refCheckResult.error, code: refCheckResult.code });
131
- }
132
- const refCheck = refCheckResult;
133
- if ((activeProvider === "grok" || activeProvider === "agy" || activeProvider === "grok-api" || activeProvider === "gemini-api") && refCheck.refs.length > 3) {
134
- finishStatus = "error";
135
- finishHttpStatus = 400;
136
- finishErrorCode = activeProvider === "agy" ? "AGY_REF_TOO_MANY" : "GROK_REF_TOO_MANY";
137
- return res.status(400).json({
138
- error: `${activeProvider === "agy" ? "Agy" : "Grok"} image editing supports up to 3 reference images`,
139
- code: activeProvider === "agy" ? "AGY_REF_TOO_MANY" : "GROK_REF_TOO_MANY",
129
+ if (started && isStartJobFailure(started)) {
130
+ const status = started.code === "TOO_MANY_JOBS" ? 429 : 409;
131
+ if (started.code === "TOO_MANY_JOBS") {
132
+ res.setHeader("Retry-After", String(INFLIGHT_RETRY_AFTER_SECONDS));
133
+ }
134
+ return fail(status, {
135
+ error: started.code === "TOO_MANY_JOBS"
136
+ ? "Too many concurrent generation jobs"
137
+ : "Request ID already in use",
138
+ code: started.code,
140
139
  requestId,
141
140
  });
142
141
  }
142
+ registerJobAbortController(requestId, cancelController);
143
+ if (asyncMode) {
144
+ res.status(202).json({ requestId, async: true });
145
+ }
146
+ setJobPhase(requestId, "streaming");
147
+ if (asyncMode)
148
+ publish(requestId, "phase", { requestId, phase: "streaming" });
143
149
  const client = req.get("x-ima2-client") || "ui";
144
150
  const referenceDiagnostics = refCheck.referenceDiagnostics || [];
145
151
  const referenceMismatchCount = referenceDiagnostics.filter((ref) => ref.warnings?.includes("mime_mismatch")).length;
@@ -152,7 +158,7 @@ export function registerGenerateRoutes(app, ctxRaw) {
152
158
  size: effectiveSize,
153
159
  moderation,
154
160
  n: count,
155
- refs: refCheck.refs.length,
161
+ refs: providerRefCount,
156
162
  referenceBytes: referencePayload.referenceBytes,
157
163
  referenceMismatchCount,
158
164
  refDetectedMimes: [...new Set(referenceDiagnostics.map((ref) => ref.detectedMime).filter(Boolean))].join(","),
@@ -175,8 +181,8 @@ export function registerGenerateRoutes(app, ctxRaw) {
175
181
  size: effectiveSize,
176
182
  signal: cancelController.signal,
177
183
  requestId,
178
- referenceCount: refCheck.refs.length,
179
- references: refCheck.refDetails,
184
+ referenceCount: grokRefs.length,
185
+ references: grokRefs,
180
186
  directApiKey: grokDirectApiKey,
181
187
  })
182
188
  : null;
@@ -210,7 +216,7 @@ export function registerGenerateRoutes(app, ctxRaw) {
210
216
  requestId,
211
217
  plannedPrompt: sharedGrokPlan?.prompt,
212
218
  webSearchCalls: sharedGrokPlan?.webSearchCalls,
213
- references: refCheck.refDetails,
219
+ references: grokRefs,
214
220
  directApiKey: grokDirectApiKey,
215
221
  });
216
222
  throwIfJobCanceled(requestId);
@@ -275,6 +281,11 @@ export function registerGenerateRoutes(app, ctxRaw) {
275
281
  }
276
282
  const rand = randomBytes(ctx.config.ids.generatedHexBytes).toString("hex");
277
283
  const filename = `${Date.now()}_${rand}_${images.length}.${resultFormat}`;
284
+ const createdAt = Date.now();
285
+ const valueWithProviderUrl = r.value;
286
+ const providerUrl = typeof valueWithProviderUrl.providerUrl === "string"
287
+ ? valueWithProviderUrl.providerUrl
288
+ : undefined;
278
289
  const meta = {
279
290
  kind: "classic",
280
291
  requestId,
@@ -293,11 +304,12 @@ export function registerGenerateRoutes(app, ctxRaw) {
293
304
  model: activeProvider === "grok" ? (quality === "high" ? "grok-imagine-image-quality" : imageModel) : imageModel,
294
305
  reasoningEffort,
295
306
  provider: activeProvider,
296
- createdAt: Date.now(),
307
+ createdAt,
308
+ ...(providerUrl ? { providerUrl } : {}),
297
309
  usage: r.value.usage || null,
298
310
  webSearchCalls: r.value.webSearchCalls || 0,
299
311
  webSearchEnabled,
300
- refsCount: refCheck.refs.length,
312
+ refsCount: providerRefCount,
301
313
  };
302
314
  const rawBuffer = Buffer.from(r.value.b64, "base64");
303
315
  const embedded = await embedImageMetadataBestEffort(rawBuffer, resultFormat, meta, {
@@ -320,6 +332,8 @@ export function registerGenerateRoutes(app, ctxRaw) {
320
332
  image: `data:${resultMime};base64,${r.value.b64}`,
321
333
  filename,
322
334
  revisedPrompt: r.value.revisedPrompt || null,
335
+ ...(providerUrl ? { providerUrl } : {}),
336
+ createdAt,
323
337
  });
324
338
  if (r.value.usage) {
325
339
  const usageValue = r.value.usage;
@@ -349,47 +363,20 @@ export function registerGenerateRoutes(app, ctxRaw) {
349
363
  const status = firstErr.status || 500;
350
364
  if (isGenerationCanceledError(firstErr)) {
351
365
  finishCanceled = true;
352
- finishHttpStatus = firstErr.status;
353
- finishErrorCode = firstErr.code;
354
- return res.status(firstErr.status).json({
366
+ return fail(firstErr.status, {
355
367
  error: firstErr.message,
356
368
  code: firstErr.code,
357
369
  requestId,
358
370
  });
359
371
  }
360
- finishStatus = "error";
361
- finishHttpStatus = status;
362
- finishErrorCode = firstErr.code;
363
- return res.status(status).json({
372
+ return fail(status, {
364
373
  error: firstErr.message,
365
374
  code: firstErr.code,
366
- upstreamCode: firstErr.upstreamCode || null,
367
- upstreamType: firstErr.upstreamType || null,
368
- upstreamParam: firstErr.upstreamParam || null,
369
- diagnosticReason: firstErr.diagnosticReason || null,
370
- retryKind: firstErr.retryKind || null,
371
- initialEventCount: firstErr.initialEventCount ?? null,
372
- initialEventTypes: firstErr.initialEventTypes || null,
373
- referencesDroppedOnRetry: firstErr.referencesDroppedOnRetry ?? null,
374
- developerPromptDroppedOnRetry: firstErr.developerPromptDroppedOnRetry ?? null,
375
- webSearchDroppedOnRetry: firstErr.webSearchDroppedOnRetry ?? null,
376
- fallbackEventCount: firstErr.fallbackEventCount ?? null,
377
- fallbackEventTypes: firstErr.fallbackEventTypes || null,
378
- fallbackImageCallSeen: firstErr.fallbackImageCallSeen ?? null,
379
- fallbackImageResultCount: firstErr.fallbackImageResultCount ?? null,
380
- errorEventCount: firstErr.eventCount ?? null,
381
- eventTypes: firstErr.eventTypes || null,
382
- webSearchCalls: firstErr.webSearchCalls ?? null,
383
- responseDiagnostics: firstErr.responseDiagnostics || null,
384
- toolTypes: firstErr.toolTypes || null,
385
- toolChoiceKind: firstErr.toolChoiceKind || null,
375
+ ...upstreamErrorFields(firstErr),
386
376
  requestId,
387
377
  });
388
378
  }
389
- finishStatus = "error";
390
- finishHttpStatus = 500;
391
- finishErrorCode = "GENERATE_ALL_FAILED";
392
- return res.status(500).json({ error: "All generation attempts failed" });
379
+ return fail(500, { error: "All generation attempts failed", code: "GENERATE_ALL_FAILED", requestId });
393
380
  }
394
381
  const elapsed = +((Date.now() - startTime) / 1000).toFixed(1);
395
382
  // Persist elapsed (computed after the generation loop) into each image's sidecar.
@@ -431,7 +418,15 @@ export function registerGenerateRoutes(app, ctxRaw) {
431
418
  elapsedMs: Date.now() - startTime,
432
419
  filename: images[0].filename,
433
420
  });
434
- res.json({ image: images[0].image, elapsed, filename: images[0].filename, requestId, ...extra });
421
+ succeed({
422
+ image: images[0].image,
423
+ elapsed,
424
+ filename: images[0].filename,
425
+ requestId,
426
+ providerUrl: images[0].providerUrl ?? null,
427
+ createdAt: images[0].createdAt,
428
+ ...extra,
429
+ });
435
430
  }
436
431
  else {
437
432
  finishHttpStatus = 200;
@@ -441,7 +436,7 @@ export function registerGenerateRoutes(app, ctxRaw) {
441
436
  imageCount: images.length,
442
437
  elapsedMs: Date.now() - startTime,
443
438
  });
444
- res.json({ images, elapsed, count: images.length, requestId, ...extra });
439
+ succeed({ images, elapsed, count: images.length, requestId, ...extra });
445
440
  }
446
441
  }
447
442
  catch (e) {
@@ -451,41 +446,18 @@ export function registerGenerateRoutes(app, ctxRaw) {
451
446
  if (isGenerationCanceledError(err.raw) || isJobCanceled(requestId)) {
452
447
  const canceled = makeGenerationCanceledError();
453
448
  finishCanceled = true;
454
- finishHttpStatus = canceled.status;
455
- finishErrorCode = canceled.code;
456
- return res.status(canceled.status).json({
449
+ return fail(canceled.status, {
457
450
  error: canceled.message,
458
451
  code: canceled.code,
459
452
  requestId,
460
453
  });
461
454
  }
462
- finishStatus = "error";
463
- finishHttpStatus = err.status || 500;
464
455
  finishErrorCode = fallbackCode || "GENERATE_FAILED";
465
456
  logError("generate", "error", err.raw, { requestId, code: finishErrorCode });
466
- res.status(err.status || 500).json({
457
+ fail(err.status || 500, {
467
458
  error: err.message,
468
- code: fallbackCode,
469
- upstreamCode: ext.upstreamCode || null,
470
- upstreamType: ext.upstreamType || null,
471
- upstreamParam: ext.upstreamParam || null,
472
- diagnosticReason: ext.diagnosticReason || null,
473
- retryKind: ext.retryKind || null,
474
- initialEventCount: ext.initialEventCount ?? null,
475
- initialEventTypes: ext.initialEventTypes || null,
476
- referencesDroppedOnRetry: ext.referencesDroppedOnRetry ?? null,
477
- developerPromptDroppedOnRetry: ext.developerPromptDroppedOnRetry ?? null,
478
- webSearchDroppedOnRetry: ext.webSearchDroppedOnRetry ?? null,
479
- fallbackEventCount: ext.fallbackEventCount ?? null,
480
- fallbackEventTypes: ext.fallbackEventTypes || null,
481
- fallbackImageCallSeen: ext.fallbackImageCallSeen ?? null,
482
- fallbackImageResultCount: ext.fallbackImageResultCount ?? null,
483
- errorEventCount: ext.eventCount ?? null,
484
- eventTypes: ext.eventTypes || null,
485
- webSearchCalls: ext.webSearchCalls ?? null,
486
- responseDiagnostics: ext.responseDiagnostics || null,
487
- toolTypes: ext.toolTypes || null,
488
- toolChoiceKind: ext.toolChoiceKind || null,
459
+ code: finishErrorCode,
460
+ ...upstreamErrorFields(ext),
489
461
  requestId,
490
462
  });
491
463
  }
package/routes/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { registerCapabilitiesRoutes } from "./capabilities.js";
2
+ import { registerEventsRoute } from "./events.js";
2
3
  import { registerHealthRoutes } from "./health.js";
3
4
  import { registerHistoryRoutes } from "./history.js";
4
5
  import { registerSessionRoutes } from "./sessions.js";
@@ -18,6 +19,7 @@ import { registerImageImportRoutes } from "./imageImport.js";
18
19
  import { registerPromptBuilderRoutes } from "./promptBuilder.js";
19
20
  import { registerAgentRoutes } from "./agent.js";
20
21
  import { registerGrokRoutes } from "./grok.js";
22
+ import { registerAgyRoutes } from "./agy.js";
21
23
  import { registerVideoRoutes } from "./video.js";
22
24
  import { registerVideoExtendedRoutes } from "./videoExtended.js";
23
25
  import { registerQuotaRoutes } from "./quota.js";
@@ -26,6 +28,7 @@ import { mountKeyRoutes } from "./keys.js";
26
28
  import { requireRuntimeContext } from "../lib/runtimeContext.js";
27
29
  export function configureRoutes(app, ctxRaw) {
28
30
  const ctx = requireRuntimeContext(ctxRaw);
31
+ registerEventsRoute(app, ctx);
29
32
  registerHealthRoutes(app, ctx);
30
33
  registerCapabilitiesRoutes(app, ctx);
31
34
  registerStorageRoutes(app, ctx);
@@ -47,6 +50,7 @@ export function configureRoutes(app, ctxRaw) {
47
50
  registerPromptRoutes(app, ctx);
48
51
  registerPromptImportRoutes(app, ctx);
49
52
  registerGrokRoutes(app, ctx);
53
+ registerAgyRoutes(app);
50
54
  registerVideoRoutes(app, ctx);
51
55
  registerVideoExtendedRoutes(app, ctx);
52
56
  registerQuotaRoutes(app, ctx);