diff --git a/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs b/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs
index 9fa3226f..8d77c4da 100644
--- a/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs
+++ b/dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs
@@ -566,6 +566,25 @@ await Upstream.Pipe(
ApiClient.ApiRequest("ai-image-generate", HttpMethod.Post, null, data, null, 120000), ctx);
}
+ ///
+ /// Per-user AI image generation history (the upstream's last 20 successful
+ /// generations). The username comes ONLY from the validated code: prompts are
+ /// private to their author, so a body-supplied username would let any caller
+ /// read someone else's generation history.
+ ///
+ public static async Task AiImagesHistory(HttpContext ctx)
+ {
+ var body = await ctx.ReadBody();
+ var username = await ValidateCode(body);
+ if (username == null)
+ {
+ await ctx.SendText(401, "Unauthorized");
+ return;
+ }
+ await Upstream.Pipe(
+ ApiClient.ApiRequest($"users/{username}/ai-images", HttpMethod.Get), ctx);
+ }
+
public static async Task AiAssistPrice(HttpContext ctx)
{
var body = await ctx.ReadBody();
diff --git a/dotnet/EcencyApi/Handlers/Routes.cs b/dotnet/EcencyApi/Handlers/Routes.cs
index bcff2f2c..dd74c8d6 100644
--- a/dotnet/EcencyApi/Handlers/Routes.cs
+++ b/dotnet/EcencyApi/Handlers/Routes.cs
@@ -155,6 +155,7 @@ public static void Map(WebApplication app)
app.MapPost("/private-api/boosted-post", PrivateApi.BoostedPost);
app.MapPost("/private-api/ai-generate-price", PrivateApi.AiGeneratePrice);
app.MapPost("/private-api/ai-generate-image", PrivateApi.AiGenerateImage);
+ app.MapPost("/private-api/ai-images", PrivateApi.AiImagesHistory);
app.MapPost("/private-api/ai-assist-price", PrivateApi.AiAssistPrice);
app.MapPost("/private-api/ai-assist", PrivateApi.AiAssist);
app.MapPost("/private-api/ai-transcribe-price", PrivateApi.AiTranscribePrice);
diff --git a/dotnet/parity/driver.py b/dotnet/parity/driver.py
index 30948790..c65239e3 100644
--- a/dotnet/parity/driver.py
+++ b/dotnet/parity/driver.py
@@ -224,6 +224,13 @@ def norm_body(text):
"::pop and ::badcode for every POST route, which is why all three appear here."
)
+AI_IMAGES_DIVERGENCE = (
+ "Per-user AI image history route added after the port (ePoints users//ai-images). "
+ "The reference build has no such route and answers 404; this one validates the "
+ "signed code and answers 401 or proxies the history. Deterministic and additive -- "
+ "no behavior the reference ever had is changing."
+)
+
# Cases where the C# port intentionally differs from Node (Node bugs the port fixes).
KNOWN_DIVERGENCES = {
"/auth-api/hs-token-refresh::min":
@@ -256,6 +263,15 @@ def norm_body(text):
AI_TRANSCRIBE_DIVERGENCE,
"/private-api/ai-transcribe::badcode":
AI_TRANSCRIBE_DIVERGENCE,
+ # Per-user AI image history, added after the port (ePoints users//ai-images).
+ # Same shape as the dictation entries: the reference build has no such route and
+ # answers 404 while this one validates the code and answers 401/proxies. Additive.
+ "/private-api/ai-images::min":
+ AI_IMAGES_DIVERGENCE,
+ "/private-api/ai-images::pop":
+ AI_IMAGES_DIVERGENCE,
+ "/private-api/ai-images::badcode":
+ AI_IMAGES_DIVERGENCE,
}
# Deliberately NOT listed above: /wallet-api/portfolio-v2::pop, whose HP action list