@agent-native/core 0.182.1-nightly-20260917183518 → 0.182.1

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 (56) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/CHANGELOG.md +6 -0
  3. package/corpus/templates/calendar/CHANGELOG.md +1 -0
  4. package/corpus/templates/clips/CHANGELOG.md +7 -0
  5. package/corpus/templates/clips/app/components/library/sort-menu.tsx +2 -2
  6. package/corpus/templates/clips/changelog/2026-09-17-clips-waits-for-authentication-before-showing-the-recording-.md +6 -0
  7. package/corpus/templates/clips/changelog/2026-09-17-the-clips-sort-control-keeps-its-keyboard-focus-ring-visible.md +6 -0
  8. package/corpus/templates/clips/chrome-extension/src/popup.html +1 -1
  9. package/corpus/templates/clips/chrome-extension/src/popup.ts +30 -21
  10. package/corpus/templates/content/CHANGELOG.md +7 -0
  11. package/corpus/templates/content/actions/resolve-preview-document-draft.ts +181 -16
  12. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +28 -15
  13. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +11 -0
  14. package/corpus/templates/content/app/components/editor/document-save-rebase.ts +13 -10
  15. package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +80 -0
  16. package/corpus/templates/content/changelog/2026-09-16-recovery-choices-stay-attached-to-the-draft-you-reviewed.md +6 -0
  17. package/corpus/templates/design/CHANGELOG.md +29 -0
  18. package/corpus/templates/design/actions/import-design-source.ts +42 -2
  19. package/corpus/templates/design/app/components/design/DesignImportPanel.tsx +26 -15
  20. package/corpus/templates/design/app/components/design/edit-panel/code-inspect-helpers.tsx +83 -1
  21. package/corpus/templates/design/app/i18n-data.ts +3 -1
  22. package/corpus/templates/design/app/lib/design-file-upload.ts +6 -4
  23. package/corpus/templates/design/app/lib/design-import.ts +2 -0
  24. package/corpus/templates/design/app/lib/fig-client-import.ts +207 -49
  25. package/corpus/templates/design/changelog/2026-09-17-large-fig-files-import-directly-in-the-browser-without-the-o.md +6 -0
  26. package/corpus/templates/design/server/lib/fig-file-decoder.ts +31 -8
  27. package/corpus/templates/design/server/lib/import-design-files.ts +188 -75
  28. package/corpus/templates/design/server/plugins/db.ts +16 -7
  29. package/corpus/templates/design/shared/fig-to-frames.ts +55 -25
  30. package/corpus/templates/factory/CHANGELOG.md +6 -0
  31. package/corpus/templates/mail/CHANGELOG.md +6 -0
  32. package/corpus/templates/slides/CHANGELOG.md +12 -0
  33. package/corpus/templates/slides/app/components/editor/AssetLibraryPanel.tsx +1 -1
  34. package/corpus/templates/slides/app/components/editor/ImageDropPromptPopover.tsx +1 -2
  35. package/corpus/templates/slides/app/hooks/use-agent-generating.ts +82 -2
  36. package/corpus/templates/slides/app/lib/image-drop-to-agent.ts +9 -2
  37. package/corpus/templates/slides/app/lib/slide-image-replacement.ts +1 -1
  38. package/corpus/templates/slides/app/pages/DeckEditor.tsx +1 -1
  39. package/corpus/templates/slides/server/handlers/assets.ts +134 -8
  40. package/corpus/templates/slides/server/handlers/uploads.ts +23 -10
  41. package/corpus/templates/slides/shared/upload-types.ts +1 -0
  42. package/dist/application-state/store.d.ts +9 -0
  43. package/dist/application-state/store.js +18 -0
  44. package/dist/automation/index.d.ts +1 -1
  45. package/dist/collab/struct-routes.d.ts +1 -1
  46. package/dist/deploy/build.js +7 -0
  47. package/dist/file-upload/actions/upload-image.d.ts +41 -10
  48. package/dist/file-upload/actions/upload-image.js +399 -19
  49. package/dist/jobs/scheduler.js +11 -0
  50. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  51. package/dist/provider-api/actions/custom-provider-registration.d.ts +7 -7
  52. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  53. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  54. package/dist/resources/handlers.d.ts +1 -1
  55. package/dist/secrets/routes.d.ts +3 -3
  56. package/package.json +4 -4
@@ -1,8 +1,11 @@
1
+ import { randomUUID } from "node:crypto";
1
2
  import { z } from "zod";
2
3
  import { defineAction } from "../../action.js";
4
+ import { compareAndSetAppState, readAppState, } from "../../application-state/index.js";
5
+ import { appStateCompareAndSet, appStateGet, appStateListByKeyPrefix, } from "../../application-state/store.js";
3
6
  import { ssrfSafeFetch } from "../../extensions/url-safety.js";
4
- import { getRequestUserEmail } from "../../server/request-context.js";
5
- import { uploadFile } from "../registry.js";
7
+ import { getRequestOrgId, getRequestUserEmail, runWithRequestContext, } from "../../server/request-context.js";
8
+ import { deleteUploadedFile, uploadFile } from "../registry.js";
6
9
  const MAX_REMOTE_FETCH_BYTES = 25 * 1024 * 1024;
7
10
  const SUPPORTED_IMAGE_MIME_TYPES = new Set([
8
11
  "image/png",
@@ -15,6 +18,314 @@ const SUPPORTED_IMAGE_MIME_TYPES = new Set([
15
18
  "image/heic",
16
19
  "image/heif",
17
20
  ]);
21
+ export const UPLOAD_RECEIPT_PREFIX = "file-upload-receipt:";
22
+ const UPLOAD_RECEIPT_STAGED_TTL_MS = 24 * 60 * 60 * 1000;
23
+ const UPLOAD_RECEIPT_PENDING_TTL_MS = 5 * 60 * 1000;
24
+ const UPLOAD_RECEIPT_DELETE_LEASE_MS = 5 * 60 * 1000;
25
+ const UPLOAD_RECEIPT_RETRY_DELAY_MS = 15 * 60 * 1000;
26
+ const UPLOAD_RECEIPT_CLEANUP_BATCH_SIZE = 50;
27
+ const UPLOAD_RECEIPT_CLEANUP_INTERVAL_MS = 15 * 60 * 1000;
28
+ const UPLOAD_RECEIPT_RESERVATION_WAIT_MS = 5_000;
29
+ const UPLOAD_RECEIPT_RESERVATION_POLL_MS = 100;
30
+ const UPLOAD_RECEIPT_IMPORT_BATCH_LIMIT = 2_048;
31
+ let uploadReceiptCleanupLastRunAt = 0;
32
+ function uploadReceiptKey(idempotencyKey) {
33
+ return `${UPLOAD_RECEIPT_PREFIX}${idempotencyKey}`;
34
+ }
35
+ function parseUploadReceipt(value) {
36
+ const status = value.status ?? "staged";
37
+ if (typeof value.filename !== "string" ||
38
+ !["pending", "staged", "committed", "deleting"].includes(String(status)) ||
39
+ (value.url !== undefined && typeof value.url !== "string") ||
40
+ (value.provider !== undefined && typeof value.provider !== "string") ||
41
+ (value.id !== undefined && typeof value.id !== "string") ||
42
+ (value.expiresAt !== undefined &&
43
+ (typeof value.expiresAt !== "number" ||
44
+ !Number.isFinite(value.expiresAt)))) {
45
+ throw new Error("Stored image upload receipt is invalid.");
46
+ }
47
+ const normalizedStatus = status;
48
+ if (normalizedStatus !== "pending" &&
49
+ (typeof value.url !== "string" || typeof value.provider !== "string")) {
50
+ throw new Error("Stored image upload receipt is missing provider data.");
51
+ }
52
+ if (normalizedStatus === "pending" &&
53
+ typeof value.reservationId !== "string") {
54
+ throw new Error("Stored image upload reservation is invalid.");
55
+ }
56
+ return {
57
+ status: normalizedStatus,
58
+ ...(typeof value.url === "string" ? { url: value.url } : {}),
59
+ ...(typeof value.provider === "string" ? { provider: value.provider } : {}),
60
+ filename: value.filename,
61
+ ...(typeof value.id === "string" ? { id: value.id } : {}),
62
+ expiresAt: typeof value.expiresAt === "number"
63
+ ? value.expiresAt
64
+ : Date.now() + UPLOAD_RECEIPT_STAGED_TTL_MS,
65
+ ...(typeof value.reservationId === "string"
66
+ ? { reservationId: value.reservationId }
67
+ : {}),
68
+ ...(typeof value.leaseExpiresAt === "number"
69
+ ? { leaseExpiresAt: value.leaseExpiresAt }
70
+ : {}),
71
+ ...(typeof value.ownerEmail === "string"
72
+ ? { ownerEmail: value.ownerEmail }
73
+ : {}),
74
+ ...(typeof value.orgId === "string" ? { orgId: value.orgId } : {}),
75
+ };
76
+ }
77
+ function receiptHasProviderData(receipt) {
78
+ return (typeof receipt.url === "string" && typeof receipt.provider === "string");
79
+ }
80
+ function requestReceiptOwner() {
81
+ const ownerEmail = getRequestUserEmail() ?? undefined;
82
+ const orgId = getRequestOrgId() ?? undefined;
83
+ return {
84
+ ...(ownerEmail ? { ownerEmail } : {}),
85
+ ...(orgId ? { orgId } : {}),
86
+ };
87
+ }
88
+ function pendingUploadReceipt(filename) {
89
+ return {
90
+ status: "pending",
91
+ filename,
92
+ reservationId: randomUUID(),
93
+ expiresAt: Date.now() + UPLOAD_RECEIPT_PENDING_TTL_MS,
94
+ ...requestReceiptOwner(),
95
+ };
96
+ }
97
+ function waitForUploadReceipt() {
98
+ return new Promise((resolve) => {
99
+ setTimeout(resolve, UPLOAD_RECEIPT_RESERVATION_POLL_MS);
100
+ });
101
+ }
102
+ async function reserveUploadReceipt(idempotencyKey, filename) {
103
+ const key = uploadReceiptKey(idempotencyKey);
104
+ const deadline = Date.now() + UPLOAD_RECEIPT_RESERVATION_WAIT_MS;
105
+ const pending = pendingUploadReceipt(filename);
106
+ while (true) {
107
+ const stored = await readAppState(key);
108
+ if (!stored) {
109
+ if (await compareAndSetAppState(key, null, pending))
110
+ return { pending };
111
+ continue;
112
+ }
113
+ const receipt = parseUploadReceipt(stored);
114
+ if (receipt.status === "staged" || receipt.status === "committed") {
115
+ return { receipt };
116
+ }
117
+ const now = Date.now();
118
+ const leaseExpiresAt = receipt.status === "deleting"
119
+ ? (receipt.leaseExpiresAt ?? receipt.expiresAt)
120
+ : receipt.expiresAt;
121
+ if (leaseExpiresAt > now) {
122
+ if (now >= deadline) {
123
+ throw new Error("Image upload is still being reconciled. Please retry the import.");
124
+ }
125
+ await waitForUploadReceipt();
126
+ continue;
127
+ }
128
+ if (await compareAndSetAppState(key, stored, pending))
129
+ return { pending };
130
+ }
131
+ }
132
+ async function deleteReceiptProviderObject(receipt, sessionId) {
133
+ const ownerEmail = receipt.ownerEmail ?? sessionId;
134
+ return await runWithRequestContext({
135
+ ...(ownerEmail ? { userEmail: ownerEmail } : {}),
136
+ ...(receipt.orgId ? { orgId: receipt.orgId } : {}),
137
+ }, () => deleteUploadedFile(receipt.provider, {
138
+ url: receipt.url,
139
+ id: receipt.id,
140
+ }));
141
+ }
142
+ async function cleanupUnrecordedUpload(receipt) {
143
+ try {
144
+ if (await deleteReceiptProviderObject(receipt))
145
+ return;
146
+ }
147
+ catch (error) {
148
+ const message = error instanceof Error ? error.message : String(error);
149
+ throw new Error(`Image upload cleanup failed: ${message}`);
150
+ }
151
+ throw new Error("Image upload cleanup failed: the provider kept the object.");
152
+ }
153
+ async function settleUploadReceipt(idempotencyKey, cleanup) {
154
+ const key = uploadReceiptKey(idempotencyKey);
155
+ for (let attempt = 0; attempt < 3; attempt += 1) {
156
+ const stored = await readAppState(key);
157
+ if (!stored)
158
+ return { idempotencyKey, alreadyMissing: true };
159
+ const receipt = parseUploadReceipt(stored);
160
+ if (cleanup === "release") {
161
+ if (receipt.status === "committed") {
162
+ return { idempotencyKey, released: true };
163
+ }
164
+ if (receipt.status !== "staged") {
165
+ return { idempotencyKey, released: false };
166
+ }
167
+ const committed = {
168
+ ...receipt,
169
+ status: "committed",
170
+ expiresAt: Date.now() + UPLOAD_RECEIPT_STAGED_TTL_MS,
171
+ };
172
+ if (await compareAndSetAppState(key, stored, committed)) {
173
+ return { idempotencyKey, released: true };
174
+ }
175
+ continue;
176
+ }
177
+ if (receipt.status === "pending") {
178
+ if (receipt.expiresAt > Date.now()) {
179
+ return { idempotencyKey, deleted: false };
180
+ }
181
+ const released = await compareAndSetAppState(key, stored, null);
182
+ return released
183
+ ? { idempotencyKey, released: true }
184
+ : { idempotencyKey, deleted: false };
185
+ }
186
+ if (!receiptHasProviderData(receipt)) {
187
+ throw new Error("Stored image upload receipt is missing provider data.");
188
+ }
189
+ if (receipt.status === "deleting" &&
190
+ (receipt.leaseExpiresAt ?? receipt.expiresAt) > Date.now()) {
191
+ return { idempotencyKey, deleted: false };
192
+ }
193
+ const deleting = {
194
+ ...receipt,
195
+ status: "deleting",
196
+ expiresAt: Date.now() + UPLOAD_RECEIPT_DELETE_LEASE_MS,
197
+ leaseExpiresAt: Date.now() + UPLOAD_RECEIPT_DELETE_LEASE_MS,
198
+ };
199
+ if (!(await compareAndSetAppState(key, stored, deleting)))
200
+ continue;
201
+ const deleted = await deleteReceiptProviderObject(receipt);
202
+ if (!deleted) {
203
+ await compareAndSetAppState(key, deleting, {
204
+ ...receipt,
205
+ status: "staged",
206
+ expiresAt: Date.now() + UPLOAD_RECEIPT_RETRY_DELAY_MS,
207
+ });
208
+ return { idempotencyKey, deleted: false };
209
+ }
210
+ await compareAndSetAppState(key, deleting, null);
211
+ return { idempotencyKey, deleted: true };
212
+ }
213
+ return { idempotencyKey, deleted: false };
214
+ }
215
+ function receiptBelongsToRequest(receipt) {
216
+ const owner = requestReceiptOwner();
217
+ return ((!receipt.ownerEmail || receipt.ownerEmail === owner.ownerEmail) &&
218
+ (!receipt.orgId || receipt.orgId === owner.orgId));
219
+ }
220
+ /** Commit every image receipt for a completed browser import batch. */
221
+ export async function commitUploadReceiptsForImport(importId) {
222
+ const rows = await appStateListByKeyPrefix(`${UPLOAD_RECEIPT_PREFIX}${importId}:`, UPLOAD_RECEIPT_IMPORT_BATCH_LIMIT);
223
+ for (const row of rows) {
224
+ const receipt = parseUploadReceipt(row.value);
225
+ if (receipt.status === "committed" || !receiptBelongsToRequest(receipt)) {
226
+ continue;
227
+ }
228
+ if (receipt.status !== "staged") {
229
+ throw new Error("Image upload receipt is not ready to commit.");
230
+ }
231
+ const committed = {
232
+ ...receipt,
233
+ status: "committed",
234
+ expiresAt: Date.now() + UPLOAD_RECEIPT_STAGED_TTL_MS,
235
+ };
236
+ if (!(await appStateCompareAndSet(row.sessionId, row.key, row.value, committed))) {
237
+ const current = await appStateGet(row.sessionId, row.key);
238
+ if (!current || parseUploadReceipt(current).status !== "committed") {
239
+ throw new Error("Could not commit the image upload receipt.");
240
+ }
241
+ }
242
+ }
243
+ }
244
+ export async function runUploadReceiptCleanupOnce(options) {
245
+ const now = options?.now ?? Date.now();
246
+ if (!options?.force &&
247
+ now - uploadReceiptCleanupLastRunAt < UPLOAD_RECEIPT_CLEANUP_INTERVAL_MS) {
248
+ return { scanned: 0, deleted: 0, released: 0, failed: 0, skipped: true };
249
+ }
250
+ uploadReceiptCleanupLastRunAt = now;
251
+ const rows = await appStateListByKeyPrefix(UPLOAD_RECEIPT_PREFIX, options?.limit ?? UPLOAD_RECEIPT_CLEANUP_BATCH_SIZE);
252
+ let deleted = 0;
253
+ let released = 0;
254
+ let failed = 0;
255
+ for (const row of rows) {
256
+ let receipt;
257
+ try {
258
+ receipt = parseUploadReceipt(row.value);
259
+ }
260
+ catch {
261
+ failed += 1;
262
+ continue;
263
+ }
264
+ if (receipt.expiresAt > now)
265
+ continue;
266
+ if (receipt.status === "pending") {
267
+ if (await appStateCompareAndSet(row.sessionId, row.key, row.value, null)) {
268
+ released += 1;
269
+ }
270
+ continue;
271
+ }
272
+ if (receipt.status === "committed") {
273
+ if (await appStateCompareAndSet(row.sessionId, row.key, row.value, null)) {
274
+ released += 1;
275
+ }
276
+ continue;
277
+ }
278
+ if (!receiptHasProviderData(receipt)) {
279
+ failed += 1;
280
+ continue;
281
+ }
282
+ if (receipt.status === "deleting" &&
283
+ (receipt.leaseExpiresAt ?? receipt.expiresAt) > now) {
284
+ continue;
285
+ }
286
+ const leaseExpiresAt = now + UPLOAD_RECEIPT_DELETE_LEASE_MS;
287
+ const deleting = {
288
+ ...receipt,
289
+ status: "deleting",
290
+ expiresAt: leaseExpiresAt,
291
+ leaseExpiresAt,
292
+ };
293
+ if (!(await appStateCompareAndSet(row.sessionId, row.key, row.value, deleting))) {
294
+ continue;
295
+ }
296
+ let objectDeleted = false;
297
+ try {
298
+ objectDeleted = await deleteReceiptProviderObject(receipt, row.sessionId);
299
+ }
300
+ catch (error) {
301
+ console.error("[file-upload] Failed to delete an expired upload receipt:", error);
302
+ }
303
+ if (objectDeleted) {
304
+ if (await appStateCompareAndSet(row.sessionId, row.key, deleting, null)) {
305
+ deleted += 1;
306
+ }
307
+ continue;
308
+ }
309
+ failed += 1;
310
+ try {
311
+ await appStateCompareAndSet(row.sessionId, row.key, deleting, {
312
+ ...receipt,
313
+ status: "staged",
314
+ expiresAt: now + UPLOAD_RECEIPT_RETRY_DELAY_MS,
315
+ });
316
+ }
317
+ catch (error) {
318
+ console.error("[file-upload] Failed to requeue an expired upload receipt:", error);
319
+ }
320
+ }
321
+ return {
322
+ scanned: rows.length,
323
+ deleted,
324
+ released,
325
+ failed,
326
+ skipped: false,
327
+ };
328
+ }
18
329
  function extensionFromMime(mimeType) {
19
330
  const bare = mimeType.split(";")[0].trim().toLowerCase();
20
331
  if (bare === "image/jpeg" || bare === "image/jpg")
@@ -139,11 +450,17 @@ export default defineAction({
139
450
  .string()
140
451
  .optional()
141
452
  .describe("Optional filename hint, used by the provider for display and to derive an extension when missing."),
453
+ idempotencyKey: z.string().trim().min(1).max(200).optional(),
454
+ cleanup: z.enum(["delete", "release"]).optional(),
142
455
  })
143
- .refine((args) => !!args.data || !!args.url, {
144
- message: "Either `data` or `url` is required.",
456
+ .refine((args) => args.cleanup ? !!args.idempotencyKey : !!args.data || !!args.url, {
457
+ message: "Either a data/url upload or an idempotencyKey cleanup is required.",
145
458
  }),
146
459
  run: async (args) => {
460
+ const idempotencyKey = args.idempotencyKey?.trim();
461
+ if (args.cleanup && idempotencyKey) {
462
+ return settleUploadReceipt(idempotencyKey, args.cleanup);
463
+ }
147
464
  let bytes;
148
465
  let mimeType;
149
466
  if (args.data) {
@@ -161,24 +478,87 @@ export default defineAction({
161
478
  };
162
479
  }
163
480
  const filename = (args.filename || defaultFilename(mimeType)).trim();
481
+ let pendingReceipt;
482
+ if (idempotencyKey) {
483
+ const reserved = await reserveUploadReceipt(idempotencyKey, filename);
484
+ if (reserved.receipt) {
485
+ if (!receiptHasProviderData(reserved.receipt)) {
486
+ throw new Error("Stored image upload receipt is missing provider data.");
487
+ }
488
+ return {
489
+ url: reserved.receipt.url,
490
+ id: reserved.receipt.id,
491
+ provider: reserved.receipt.provider,
492
+ };
493
+ }
494
+ pendingReceipt = reserved.pending;
495
+ }
164
496
  const ownerEmail = getRequestUserEmail() ?? undefined;
165
- const result = await uploadFile({
166
- data: bytes,
167
- filename,
168
- mimeType,
169
- ownerEmail,
170
- });
171
- if (!result) {
497
+ try {
498
+ const result = await uploadFile({
499
+ data: bytes,
500
+ filename,
501
+ mimeType,
502
+ ownerEmail,
503
+ });
504
+ if (!result) {
505
+ if (pendingReceipt && idempotencyKey) {
506
+ await compareAndSetAppState(uploadReceiptKey(idempotencyKey), pendingReceipt, null);
507
+ }
508
+ return {
509
+ error: uploadNotConfiguredError(),
510
+ configured: false,
511
+ connectPath: "/_agent-native/builder/connect",
512
+ };
513
+ }
514
+ if (pendingReceipt && idempotencyKey) {
515
+ const stagedReceipt = {
516
+ ...pendingReceipt,
517
+ status: "staged",
518
+ url: result.url,
519
+ ...(result.id ? { id: result.id } : {}),
520
+ provider: result.provider,
521
+ expiresAt: Date.now() + UPLOAD_RECEIPT_STAGED_TTL_MS,
522
+ };
523
+ const unrecordedReceipt = {
524
+ ...stagedReceipt,
525
+ url: result.url,
526
+ provider: result.provider,
527
+ };
528
+ const staged = await compareAndSetAppState(uploadReceiptKey(idempotencyKey), pendingReceipt, stagedReceipt);
529
+ if (!staged) {
530
+ const current = await readAppState(uploadReceiptKey(idempotencyKey));
531
+ if (current) {
532
+ const existing = parseUploadReceipt(current);
533
+ if (receiptHasProviderData(existing)) {
534
+ await cleanupUnrecordedUpload(unrecordedReceipt);
535
+ return {
536
+ url: existing.url,
537
+ id: existing.id,
538
+ provider: existing.provider,
539
+ };
540
+ }
541
+ }
542
+ await cleanupUnrecordedUpload(unrecordedReceipt);
543
+ throw new Error("Could not record the image upload receipt.");
544
+ }
545
+ }
172
546
  return {
173
- error: uploadNotConfiguredError(),
174
- configured: false,
175
- connectPath: "/_agent-native/builder/connect",
547
+ url: result.url,
548
+ id: result.id,
549
+ provider: result.provider,
176
550
  };
177
551
  }
178
- return {
179
- url: result.url,
180
- id: result.id,
181
- provider: result.provider,
182
- };
552
+ catch (error) {
553
+ if (pendingReceipt && idempotencyKey) {
554
+ try {
555
+ await compareAndSetAppState(uploadReceiptKey(idempotencyKey), pendingReceipt, null);
556
+ }
557
+ catch (cleanupError) {
558
+ console.error("[file-upload] Failed to release upload reservation:", cleanupError);
559
+ }
560
+ }
561
+ throw error;
562
+ }
183
563
  },
184
564
  });
@@ -112,6 +112,17 @@ export async function processRecurringJobs(deps) {
112
112
  }
113
113
  async function processRecurringJobsWithLease(deps) {
114
114
  subscribeToJobsResourceEvents();
115
+ // Upload receipts are framework-owned temporary state, so the same durable
116
+ // scheduler sweep that runs on serverless hosts also expires abandoned
117
+ // provider objects. The cleanup is internally throttled and never blocks
118
+ // recurring jobs when a provider or database is unavailable.
119
+ try {
120
+ const { runUploadReceiptCleanupOnce } = await import("../file-upload/actions/upload-image.js");
121
+ await runUploadReceiptCleanupOnce();
122
+ }
123
+ catch (error) {
124
+ console.error("[recurring-jobs] Upload receipt cleanup failed:", error);
125
+ }
115
126
  // Skip if we recently confirmed there are no job resources to run.
116
127
  const nowMs = Date.now();
117
128
  // Write a global heartbeat before the resource scan. A slow or failed scan
@@ -137,13 +137,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
137
137
  inputSchema: {
138
138
  type: string;
139
139
  properties: {
140
- count?: undefined;
141
140
  query?: undefined;
142
141
  minutes?: undefined;
143
142
  limit?: undefined;
144
143
  clientHint?: undefined;
145
144
  timestamp?: undefined;
146
145
  chapterId?: undefined;
146
+ count?: undefined;
147
147
  startAt?: undefined;
148
148
  endAt?: undefined;
149
149
  reason?: undefined;
@@ -159,7 +159,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
159
159
  inputSchema: {
160
160
  type: string;
161
161
  properties: {
162
- count?: undefined;
163
162
  query: {
164
163
  type: string;
165
164
  description: string;
@@ -175,6 +174,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
175
174
  clientHint?: undefined;
176
175
  timestamp?: undefined;
177
176
  chapterId?: undefined;
177
+ count?: undefined;
178
178
  startAt?: undefined;
179
179
  endAt?: undefined;
180
180
  reason?: undefined;
@@ -190,7 +190,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
190
190
  inputSchema: {
191
191
  type: string;
192
192
  properties: {
193
- count?: undefined;
194
193
  minutes: {
195
194
  type: string;
196
195
  description: string;
@@ -200,6 +199,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
200
199
  clientHint?: undefined;
201
200
  timestamp?: undefined;
202
201
  chapterId?: undefined;
202
+ count?: undefined;
203
203
  startAt?: undefined;
204
204
  endAt?: undefined;
205
205
  reason?: undefined;
@@ -216,7 +216,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
216
216
  type: string;
217
217
  required: string[];
218
218
  properties: {
219
- count?: undefined;
220
219
  query: {
221
220
  type: string;
222
221
  description: string;
@@ -235,6 +234,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
235
234
  };
236
235
  timestamp?: undefined;
237
236
  chapterId?: undefined;
237
+ count?: undefined;
238
238
  startAt?: undefined;
239
239
  endAt?: undefined;
240
240
  reason?: undefined;
@@ -250,7 +250,6 @@ export declare function screenMemoryMcpToolDefinitions(): ({
250
250
  type: string;
251
251
  required: string[];
252
252
  properties: {
253
- count?: undefined;
254
253
  query?: undefined;
255
254
  minutes?: undefined;
256
255
  limit?: undefined;
@@ -264,6 +263,7 @@ export declare function screenMemoryMcpToolDefinitions(): ({
264
263
  description: string;
265
264
  };
266
265
  chapterId?: undefined;
266
+ count?: undefined;
267
267
  startAt?: undefined;
268
268
  endAt?: undefined;
269
269
  includeMicrophone?: undefined;
@@ -314,13 +314,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
314
314
  type: string;
315
315
  required: string[];
316
316
  properties: {
317
- count?: undefined;
318
317
  query?: undefined;
319
318
  minutes?: undefined;
320
319
  limit?: undefined;
321
320
  clientHint?: undefined;
322
321
  timestamp?: undefined;
323
322
  chapterId?: undefined;
323
+ count?: undefined;
324
324
  startAt: {
325
325
  type: string;
326
326
  description: string;
@@ -349,13 +349,13 @@ export declare function screenMemoryMcpToolDefinitions(): ({
349
349
  type: string;
350
350
  required: string[];
351
351
  properties: {
352
- count?: undefined;
353
352
  query?: undefined;
354
353
  minutes?: undefined;
355
354
  limit?: undefined;
356
355
  clientHint?: undefined;
357
356
  timestamp?: undefined;
358
357
  chapterId?: undefined;
358
+ count?: undefined;
359
359
  startAt?: undefined;
360
360
  endAt?: undefined;
361
361
  reason?: undefined;
@@ -76,9 +76,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
78
  id?: undefined;
79
- provider?: undefined;
80
79
  deleted?: undefined;
81
- found?: undefined;
82
80
  providers: {
83
81
  id: string;
84
82
  label: string;
@@ -89,6 +87,8 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
89
87
  updatedAt: number;
90
88
  }[];
91
89
  count: number;
90
+ provider?: undefined;
91
+ found?: undefined;
92
92
  registered?: undefined;
93
93
  label?: undefined;
94
94
  message?: undefined;
@@ -103,31 +103,31 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
103
103
  label?: undefined;
104
104
  message?: undefined;
105
105
  } | {
106
- provider?: undefined;
107
106
  deleted?: undefined;
108
107
  providers?: undefined;
109
108
  count?: undefined;
109
+ provider?: undefined;
110
110
  found: boolean;
111
111
  id: string;
112
112
  registered?: undefined;
113
113
  label?: undefined;
114
114
  message?: undefined;
115
115
  } | {
116
- provider?: undefined;
117
- found?: undefined;
118
116
  providers?: undefined;
119
117
  count?: undefined;
118
+ provider?: undefined;
119
+ found?: undefined;
120
120
  deleted: boolean;
121
121
  id: string;
122
122
  registered?: undefined;
123
123
  label?: undefined;
124
124
  message?: undefined;
125
125
  } | {
126
- provider?: undefined;
127
126
  deleted?: undefined;
128
- found?: undefined;
129
127
  providers?: undefined;
130
128
  count?: undefined;
129
+ provider?: undefined;
130
+ found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;
@@ -312,7 +312,8 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
312
312
  scope?: "org" | "user" | undefined;
313
313
  }, {
314
314
  id?: undefined;
315
- found?: undefined;
315
+ message?: undefined;
316
+ deleted?: undefined;
316
317
  providers: {
317
318
  id: string;
318
319
  label: string;
@@ -324,46 +325,45 @@ export declare function createProviderApiActions(runtime: Pick<ProviderApiRuntim
324
325
  }[];
325
326
  count: number;
326
327
  provider?: undefined;
327
- deleted?: undefined;
328
+ found?: undefined;
328
329
  registered?: undefined;
329
330
  label?: undefined;
330
- message?: undefined;
331
331
  } | {
332
332
  id?: undefined;
333
+ message?: undefined;
334
+ deleted?: undefined;
333
335
  providers?: undefined;
334
336
  count?: undefined;
335
337
  found: boolean;
336
338
  provider: import("../custom-registry.js").CustomProviderConfig;
337
- deleted?: undefined;
338
339
  registered?: undefined;
339
340
  label?: undefined;
340
- message?: undefined;
341
341
  } | {
342
+ message?: undefined;
343
+ deleted?: undefined;
342
344
  providers?: undefined;
343
345
  count?: undefined;
344
346
  provider?: undefined;
345
347
  found: boolean;
346
348
  id: string;
347
- deleted?: undefined;
348
349
  registered?: undefined;
349
350
  label?: undefined;
350
- message?: undefined;
351
351
  } | {
352
- found?: undefined;
352
+ message?: undefined;
353
353
  providers?: undefined;
354
354
  count?: undefined;
355
355
  provider?: undefined;
356
+ found?: undefined;
356
357
  deleted: boolean;
357
358
  id: string;
358
359
  registered?: undefined;
359
360
  label?: undefined;
360
- message?: undefined;
361
361
  } | {
362
- found?: undefined;
362
+ deleted?: undefined;
363
363
  providers?: undefined;
364
364
  count?: undefined;
365
365
  provider?: undefined;
366
- deleted?: undefined;
366
+ found?: undefined;
367
367
  registered: boolean;
368
368
  id: string;
369
369
  label: string;