@lazyingart/agent-web 0.1.41 → 0.1.43

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lazyingart/agent-web",
3
- "version": "0.1.41",
3
+ "version": "0.1.43",
4
4
  "description": "Cloud presentation and delivery-index foundation for the LazyingArt agent web application",
5
5
  "repository": {
6
6
  "type": "git",
@@ -90,6 +90,7 @@ function exactLimitOverrides(input = {}) {
90
90
  bodyTimeoutMs: [50, 15_000],
91
91
  visionBodyTimeoutMs: [1_000, 300_000],
92
92
  dependencyTimeoutMs: [50, 120_000],
93
+ jobAdmissionTimeoutMs: [50, 600_000],
93
94
  jobTimeoutMs: [50, 600_000],
94
95
  visionJobTimeoutMs: [50, 900_000],
95
96
  sseLifetimeMs: [100, 120_000],
@@ -1244,19 +1245,23 @@ export function createCloudRequestHandler({
1244
1245
  if (jobs.has(jobKey)) return true;
1245
1246
  if (stopping || !directChatConnector || jobs.size >= limits.directChatJobs) return false;
1246
1247
  const scheduled = chat.getGeneration({ accountId, threadId, generationId });
1247
- const jobTimeoutMs = scheduled?.modelAlias === visionModelAlias
1248
+ const executionTimeoutMs = scheduled?.modelAlias === visionModelAlias
1248
1249
  ? limits.visionJobTimeoutMs
1249
1250
  : limits.jobTimeoutMs;
1250
1251
  const controller = new AbortController();
1251
- const job = { controller, timedOut: false, promise: null, lease: null };
1252
+ const job = { controller, timedOut: false, promise: null, lease: null, timer: null };
1252
1253
  jobs.set(jobKey, job);
1253
- const timer = setTimeout(() => {
1254
- job.timedOut = true;
1255
- const error = new Error('direct chat generation timed out');
1256
- error.name = 'TimeoutError';
1257
- controller.abort(error);
1258
- }, jobTimeoutMs);
1259
- timer.unref?.();
1254
+ const armJobTimeout = (milliseconds, phase) => {
1255
+ clearTimeout(job.timer);
1256
+ job.timer = setTimeout(() => {
1257
+ job.timedOut = true;
1258
+ const error = new Error(`direct chat generation ${phase} timed out`);
1259
+ error.name = 'TimeoutError';
1260
+ controller.abort(error);
1261
+ }, milliseconds);
1262
+ job.timer.unref?.();
1263
+ };
1264
+ armJobTimeout(limits.jobAdmissionTimeoutMs, 'admission');
1260
1265
  job.promise = (async () => {
1261
1266
  try {
1262
1267
  const thread = chat.getThread(accountId, threadId);
@@ -1338,7 +1343,10 @@ export function createCloudRequestHandler({
1338
1343
  ownerToken,
1339
1344
  fence: lease.fence
1340
1345
  });
1341
- if (marker.dispatchAuthorized === true) break;
1346
+ if (marker.dispatchAuthorized === true) {
1347
+ armJobTimeout(executionTimeoutMs, 'execution');
1348
+ break;
1349
+ }
1342
1350
  if (marker.dispatchState !== 'global_busy') {
1343
1351
  const error = new Error('inference dispatch is already ambiguous');
1344
1352
  error.failureCode = 'provider_unavailable';
@@ -1431,7 +1439,7 @@ export function createCloudRequestHandler({
1431
1439
  // Another request may have cancelled or completed the same durable generation.
1432
1440
  }
1433
1441
  } finally {
1434
- clearTimeout(timer);
1442
+ clearTimeout(job.timer);
1435
1443
  if (job.renewTimer) clearInterval(job.renewTimer);
1436
1444
  if (job.lease) {
1437
1445
  try {
@@ -42,7 +42,8 @@ export const CLOUD_HTTP_LIMITS = Object.freeze({
42
42
  bodyTimeoutMs: 5_000,
43
43
  visionBodyTimeoutMs: 240_000,
44
44
  dependencyTimeoutMs: 30_000,
45
- jobTimeoutMs: 120_000,
45
+ jobAdmissionTimeoutMs: 120_000,
46
+ jobTimeoutMs: 600_000,
46
47
  visionJobTimeoutMs: 600_000,
47
48
  sseLifetimeMs: 30_000,
48
49
  ssePollMs: 100,
@@ -912,6 +912,46 @@ function chatFailureDiagnostic(error) {
912
912
  });
913
913
  }
914
914
 
915
+ function chatNotSentRecoveryMessage(diagnostic, imageCount = 0) {
916
+ const hasImages = Number.isSafeInteger(imageCount) && imageCount > 0;
917
+ const subject = hasImages ? "This image message" : "This message";
918
+ const preserved = hasImages
919
+ ? `Your prompt and ${imageCount === 1 ? "image are" : "images are"} still ready.`
920
+ : "Your prompt is still ready.";
921
+ if (diagnostic.stage === "authentication" || diagnostic.stage === "csrf") {
922
+ return `${subject} was not sent because your sign-in expired. ${preserved} Sign in again, then retry.`;
923
+ }
924
+ if (diagnostic.stage === "body_rejection" && diagnostic.code === "request_too_large") {
925
+ return `${subject} was not sent because the upload was too large. ${preserved} Remove an image or choose smaller files, then retry.`;
926
+ }
927
+ if (diagnostic.stage === "body_rejection" && diagnostic.code === "invalid_attachment") {
928
+ return `${subject} was not sent because an image format or file was rejected. ${preserved} Replace the affected image, then retry.`;
929
+ }
930
+ if (diagnostic.stage === "authoritative_conflict") {
931
+ return `${subject} was not sent because the conversation changed. ${preserved} Reopen this conversation, then retry.`;
932
+ }
933
+ if (diagnostic.stage === "network_timeout") {
934
+ return `${subject} was not sent because the request timed out. ${preserved} Check your connection, then retry.`;
935
+ }
936
+ if (diagnostic.stage === "network") {
937
+ return `${subject} was not sent because the network was unavailable. ${preserved} Reconnect, then retry.`;
938
+ }
939
+ if (diagnostic.stage === "snapshot") {
940
+ return `${subject} was not sent because the conversation could not be refreshed. ${preserved} Check your connection, then reopen it and retry.`;
941
+ }
942
+ if (diagnostic.stage === "local_preparation") {
943
+ return `${subject} was not sent because the browser could not prepare it. ${preserved} ${hasImages
944
+ ? "Remove and re-add the images, then retry; refresh the app if it repeats."
945
+ : "Refresh the app, then retry."}`;
946
+ }
947
+ if (diagnostic.stage === "body_rejection") {
948
+ return `${subject} was not sent because the service could not accept it. ${preserved} ${hasImages
949
+ ? "Remove or replace the affected image, then retry."
950
+ : "Edit it, then retry."}`;
951
+ }
952
+ return `${subject} was not sent because the service rejected it before it ran. ${preserved} Edit it or retry when the service is available.`;
953
+ }
954
+
915
955
  function normalizedBrowserPath(value, name, { trailingSlash = false } = {}) {
916
956
  if (typeof value !== "string" || value.length > 160 || !/^\/[A-Za-z0-9._~/-]*$/u.test(value) || value.includes("//")
917
957
  || value.split("/").some((part) => part === "." || part === "..")) {
@@ -4341,9 +4381,7 @@ export function createBrowserApp({
4341
4381
  const imageRestored = restoreDetachedImage(detachedImage);
4342
4382
  detachedImage = null;
4343
4383
  const diagnostic = applyChatFailureDiagnostic(error);
4344
- showToast(imageRestored
4345
- ? `This image message was not sent. Your prompt and ${selected.length === 1 ? "image" : "images"} are still ready; edit or retry them.`
4346
- : "This message was not sent. Your prompt is still in the composer; edit it and try again.");
4384
+ showToast(chatNotSentRecoveryMessage(diagnostic, imageRestored ? selected.length : 0));
4347
4385
  if (diagnostic.reauthenticate) requireFreshAuthentication();
4348
4386
  } else if (state.mode === "chat" && state.chatPendingSend && !state.chatPendingSend.runDispatched) {
4349
4387
  elements.message_input.value = draft;