@agent-native/core 0.157.7 → 0.157.9

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 (69) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/actions/purge-first-party-analytics-postgres.ts +26 -8
  3. package/corpus/templates/analytics/scripts/first-party-analytics-postgres-retention-dry-run.sql +258 -0
  4. package/corpus/templates/analytics/server/lib/first-party-analytics-purge.ts +59 -27
  5. package/corpus/templates/analytics/server/lib/workspace-feature-flags.ts +4 -1
  6. package/corpus/templates/slides/actions/export-pptx.ts +261 -71
  7. package/corpus/templates/slides/actions/import-file.ts +12 -0
  8. package/corpus/templates/slides/actions/import-pptx.ts +9 -0
  9. package/corpus/templates/slides/app/lib/export-pptx-client.ts +45 -5
  10. package/corpus/templates/slides/server/handlers/import/html-converter.ts +69 -1
  11. package/corpus/templates/slides/server/handlers/import/pdf-fidelity-parser.ts +316 -64
  12. package/corpus/templates/slides/server/lib/source-import.ts +10 -1
  13. package/dist/agent/harness/migrations.d.ts +4 -0
  14. package/dist/agent/harness/migrations.js +39 -0
  15. package/dist/agent/production-agent.d.ts +2 -0
  16. package/dist/agent/production-agent.js +4 -0
  17. package/dist/agent/run-loop-with-resume.d.ts +1 -0
  18. package/dist/agent/run-loop-with-resume.js +45 -30
  19. package/dist/agent/run-migrations.d.ts +8 -0
  20. package/dist/agent/run-migrations.js +61 -0
  21. package/dist/chat-threads/migrations.d.ts +1 -0
  22. package/dist/chat-threads/migrations.js +1 -0
  23. package/dist/chat-threads/schema-migrations.d.ts +8 -0
  24. package/dist/chat-threads/schema-migrations.js +60 -0
  25. package/dist/client/AssistantChat.js +4 -0
  26. package/dist/client/chat/message-components.d.ts +2 -0
  27. package/dist/client/chat/message-components.js +11 -1
  28. package/dist/client/observability/ThumbsFeedback.js +35 -22
  29. package/dist/client/observability/useObservability.d.ts +1 -1
  30. package/dist/client/observability/useObservability.js +6 -3
  31. package/dist/collab/awareness.d.ts +2 -2
  32. package/dist/collab/struct-routes.d.ts +1 -1
  33. package/dist/feature-flags/a2a-action-route.js +4 -2
  34. package/dist/feature-flags/permissions.d.ts +2 -0
  35. package/dist/feature-flags/permissions.js +6 -1
  36. package/dist/ingestion/pptx.d.ts +14 -1
  37. package/dist/ingestion/pptx.js +222 -41
  38. package/dist/localization/core-messages/ar-SA.js +4 -0
  39. package/dist/localization/core-messages/de-DE.js +4 -0
  40. package/dist/localization/core-messages/en-US.d.ts +4 -0
  41. package/dist/localization/core-messages/en-US.js +4 -0
  42. package/dist/localization/core-messages/es-ES.js +4 -0
  43. package/dist/localization/core-messages/fr-FR.js +4 -0
  44. package/dist/localization/core-messages/hi-IN.js +4 -0
  45. package/dist/localization/core-messages/ja-JP.js +4 -0
  46. package/dist/localization/core-messages/ko-KR.js +4 -0
  47. package/dist/localization/core-messages/pt-BR.js +4 -0
  48. package/dist/localization/core-messages/zh-CN.js +4 -0
  49. package/dist/localization/core-messages/zh-TW.js +4 -0
  50. package/dist/localization/core-messages.d.ts +4 -0
  51. package/dist/notifications/routes.d.ts +1 -1
  52. package/dist/observability/feedback.js +40 -16
  53. package/dist/observability/routes.d.ts +1 -1
  54. package/dist/observability/routes.js +15 -3
  55. package/dist/observability/store.js +2 -0
  56. package/dist/observability/traces.js +5 -1
  57. package/dist/provider-api/actions/custom-provider-registration.d.ts +12 -12
  58. package/dist/provider-api/actions/provider-api.d.ts +10 -10
  59. package/dist/resources/handlers.d.ts +1 -1
  60. package/dist/server/action-routes.d.ts +7 -5
  61. package/dist/server/action-routes.js +9 -4
  62. package/dist/server/better-auth-instance.js +4 -0
  63. package/dist/server/poll.d.ts +4 -4
  64. package/dist/server/poll.js +45 -13
  65. package/dist/server/realtime-voice.js +2 -1
  66. package/dist/server/release-migrations.js +16 -0
  67. package/dist/usage/migrations.d.ts +4 -0
  68. package/dist/usage/migrations.js +45 -0
  69. package/package.json +3 -3
@@ -100,6 +100,7 @@ export async function importPptxBufferToDeck(args: {
100
100
  imported: true;
101
101
  url: string;
102
102
  imagesSkipped?: number;
103
+ tablesDegraded?: number;
103
104
  }> {
104
105
  const {
105
106
  fileBuffer,
@@ -156,6 +157,7 @@ export async function importPptxBufferToDeck(args: {
156
157
  sourceText: parsedSlide.texts.map((text) => text.content).join("\n"),
157
158
  imageUrls: Object.values(uploadedImages.urls),
158
159
  imageSkippedCount: uploadedImages.imageSkippedCount,
160
+ tablesDegraded: parsedSlide.tablesDegraded ?? 0,
159
161
  };
160
162
  }),
161
163
  ),
@@ -165,6 +167,10 @@ export async function importPptxBufferToDeck(args: {
165
167
  (total, r) => total + r.imageSkippedCount,
166
168
  0,
167
169
  );
170
+ const tablesDegraded = results.reduce(
171
+ (total, r) => total + r.tablesDegraded,
172
+ 0,
173
+ );
168
174
  if (imagesSkipped > 0) {
169
175
  throw new Error(
170
176
  `Source-faithful PPTX import could not preserve ${imagesSkipped} image(s). No deck was written. Retry with browser-renderable images or use a PDF export for page-faithful preservation.`,
@@ -180,6 +186,7 @@ export async function importPptxBufferToDeck(args: {
180
186
  editableText: true,
181
187
  })),
182
188
  imagesSkipped,
189
+ tablesDegraded,
183
190
  });
184
191
  const aspectRatio = nearestAspectRatio(
185
192
  presentation.slides[0]?.widthEmu,
@@ -234,6 +241,7 @@ export async function importPptxBufferToDeck(args: {
234
241
  imported: true,
235
242
  url: getDeckUrl(deckId),
236
243
  ...(imagesSkipped > 0 ? { imagesSkipped } : {}),
244
+ ...(tablesDegraded > 0 ? { tablesDegraded } : {}),
237
245
  };
238
246
  }
239
247
 
@@ -269,6 +277,7 @@ export async function importPptxBufferToDeck(args: {
269
277
  imported: true,
270
278
  url: getDeckUrl(id),
271
279
  ...(imagesSkipped > 0 ? { imagesSkipped } : {}),
280
+ ...(tablesDegraded > 0 ? { tablesDegraded } : {}),
272
281
  };
273
282
  }
274
283
 
@@ -56,6 +56,9 @@ function waitForImageToSettle(image: HTMLImageElement): Promise<void> {
56
56
  if (decodeStarted) return;
57
57
  decodeStarted = true;
58
58
  if (!image.complete || image.naturalWidth <= 0) {
59
+ console.warn(
60
+ `[export-pptx] image could not be loaded for export: ${image.src}`,
61
+ );
59
62
  finish();
60
63
  return;
61
64
  }
@@ -141,9 +144,12 @@ const NOTES_MASTER_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?
141
144
  const NOTES_MASTER_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
142
145
  <Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="../theme/theme1.xml"/></Relationships>`;
143
146
 
147
+ const EMU_PER_INCH = 914_400;
148
+
144
149
  export async function addSpeakerNotesToPptxBlob(
145
150
  blob: Blob,
146
151
  slides: PptxExportSlide[],
152
+ pptxInches: { w: number; h: number },
147
153
  ): Promise<Blob> {
148
154
  const hasNotes = slides.some((slide) => slide.notes?.trim());
149
155
  if (!hasNotes) return blob;
@@ -194,9 +200,14 @@ export async function addSpeakerNotesToPptxBlob(
194
200
  }
195
201
 
196
202
  if (!presentationXml.includes("<p:notesSz")) {
203
+ // The notes page is a portrait rotation of the slide, so its cx/cy swap
204
+ // the slide's own width/height (matches PowerPoint's own notesMaster
205
+ // output, e.g. a 13.33x7.5in 16:9 slide gets a 7.5x13.33in notes page).
206
+ const notesCx = Math.round(pptxInches.h * EMU_PER_INCH);
207
+ const notesCy = Math.round(pptxInches.w * EMU_PER_INCH);
197
208
  presentationXml = presentationXml.replace(
198
209
  "<p:defaultTextStyle>",
199
- '<p:notesSz cx="6858000" cy="12192000"/><p:defaultTextStyle>',
210
+ `<p:notesSz cx="${notesCx}" cy="${notesCy}"/><p:defaultTextStyle>`,
200
211
  );
201
212
  }
202
213
 
@@ -667,13 +678,36 @@ function normalizeSingleLineText(
667
678
  }
668
679
  }
669
680
 
681
+ const CSS_PX_PER_INCH = 96;
682
+
683
+ /**
684
+ * dom-to-pptx fits the rendered clone into the requested slide size and
685
+ * scales every measurement it takes by this same factor (dist/dom-to-pptx.mjs
686
+ * `processSlide`). The bullet indent patched in afterward must match that
687
+ * scale or it drifts off the deck's actual aspect ratio.
688
+ */
689
+ export function pptxExportScale(dims: {
690
+ width: number;
691
+ height: number;
692
+ pptxInches: { w: number; h: number };
693
+ }) {
694
+ return Math.min(
695
+ dims.pptxInches.w / (dims.width / CSS_PX_PER_INCH),
696
+ dims.pptxInches.h / (dims.height / CSS_PX_PER_INCH),
697
+ );
698
+ }
699
+
670
700
  /**
671
701
  * CSS markers live outside the LI box, so dom-to-pptx cannot infer the gap
672
702
  * between a bullet and its text from getBoundingClientRect alone. Add that
673
703
  * source-visible gap only on the export clone; the source DOM stays editable.
674
704
  */
675
- function normalizeListsForPptx(root: HTMLElement) {
705
+ function normalizeListsForPptx(
706
+ root: HTMLElement,
707
+ dims: { width: number; height: number; pptxInches: { w: number; h: number } },
708
+ ) {
676
709
  const bulletIndents: number[] = [];
710
+ const scale = pptxExportScale(dims);
677
711
 
678
712
  for (const list of root.querySelectorAll<HTMLElement>("ul,ol")) {
679
713
  const listStyle = window.getComputedStyle(list);
@@ -711,7 +745,9 @@ function normalizeListsForPptx(root: HTMLElement) {
711
745
  if (!listRect.width || !itemRect.width) continue;
712
746
 
713
747
  const visualIndentPx = itemRect.left - listRect.left;
714
- bulletIndents.push(Math.max(0, (visualIndentPx - paddingLeft) * 0.75));
748
+ bulletIndents.push(
749
+ Math.max(0, (visualIndentPx - paddingLeft) * 0.75 * scale),
750
+ );
715
751
  }
716
752
 
717
753
  return bulletIndents;
@@ -1041,7 +1077,7 @@ export async function buildDeckPptxBlob(
1041
1077
  exportClones.push(clone);
1042
1078
  await preloadImagesWithCors(clone.element);
1043
1079
  resetAutofitTransforms(clone.element);
1044
- slideBulletIndents.push(normalizeListsForPptx(clone.element));
1080
+ slideBulletIndents.push(normalizeListsForPptx(clone.element, dims));
1045
1081
  restorePositionedGeometry(
1046
1082
  clone.element,
1047
1083
  clone.sourceRect,
@@ -1083,7 +1119,11 @@ export async function buildDeckPptxBlob(
1083
1119
  initialBlob,
1084
1120
  slideBulletIndents,
1085
1121
  );
1086
- const blob = await addSpeakerNotesToPptxBlob(bulletPatchedBlob, slides);
1122
+ const blob = await addSpeakerNotesToPptxBlob(
1123
+ bulletPatchedBlob,
1124
+ slides,
1125
+ dims.pptxInches,
1126
+ );
1087
1127
  return { blob, filename: safePptxName(deckTitle) };
1088
1128
  } finally {
1089
1129
  for (const clone of exportClones) {
@@ -167,7 +167,11 @@ export function convertToSlideHtml(
167
167
 
168
168
  const DEFAULT_SLIDE_WIDTH_EMU = 9144000;
169
169
  const DEFAULT_SLIDE_HEIGHT_EMU = 5143500;
170
- const DEFAULT_PPTX_BACKGROUND = "#000000"; // guard:allow-raw-color - preserve PPTX black when no background is declared
170
+ // PowerPoint's own default slide background (no `<p:bg>` declared) is white,
171
+ // not black — defaulting to black here made an undecorated slide's own
172
+ // (often dark, theme-default) text unreadable or fully invisible against a
173
+ // background the source file never actually specified.
174
+ const DEFAULT_PPTX_BACKGROUND = "#ffffff"; // guard:allow-raw-color - PPTX's own white default when no background is declared
171
175
  const DEFAULT_PPTX_FOREGROUND = "#ffffff"; // guard:allow-raw-color - preserve PPTX white when no run color is declared
172
176
 
173
177
  /**
@@ -250,6 +254,18 @@ function buildFidelityElement(
250
254
  return `<div class="fmd-pptx-image" data-pptx-element-kind="image" data-pptx-image-name="${esc(element.image?.name ?? "image")}"${objectId} style="${position}${rotation} overflow: hidden;">${url ? `<img src="${esc(url)}" alt="" style="${imageStyle}" />` : `<div class="fmd-img-placeholder" style="width:100%;height:100%;">Imported image: ${esc(element.image?.name ?? "image")}</div>`}</div>`;
251
255
  }
252
256
 
257
+ if (element.kind === "table") {
258
+ return buildFidelityTable(
259
+ element,
260
+ widthEmu,
261
+ refBox.width,
262
+ themeFont,
263
+ position,
264
+ rotation,
265
+ objectId,
266
+ );
267
+ }
268
+
253
269
  const decoration = shapeDecoration(element, widthEmu, refBox.width);
254
270
  if (element.kind === "shape") {
255
271
  return `<div class="fmd-pptx-shape" data-pptx-element-kind="shape"${objectId} style="${position}${rotation}${decoration}"></div>`;
@@ -331,6 +347,58 @@ function imageRenderStyle(element: ParsedElement): string {
331
347
  return `display:block;position:absolute;left:${(-crop.left / visibleWidth) * 100}%;top:${(-crop.top / visibleHeight) * 100}%;width:${(1 / visibleWidth) * 100}%;height:${(1 / visibleHeight) * 100}%;object-fit:fill;`;
332
348
  }
333
349
 
350
+ type ParsedTableCell = NonNullable<
351
+ ParsedElement["table"]
352
+ >["rows"][number][number];
353
+
354
+ const DEFAULT_PPTX_TABLE_BORDER = "rgba(255,255,255,0.25)"; // guard:allow-raw-color - imported PPTX table cell border approximation, same fixed-contrast convention as the rest of this fidelity renderer
355
+
356
+ /** Render a parsed `"table"` element (a PPTX `graphicFrame`'s `a:tbl`) as a real HTML `<table>`, sized/positioned the same way every other fidelity element is. */
357
+ function buildFidelityTable(
358
+ element: ParsedElement,
359
+ widthEmu: number,
360
+ refWidthPx: number,
361
+ themeFont: string | undefined,
362
+ position: string,
363
+ rotation: string,
364
+ objectId: string,
365
+ ): string {
366
+ const rows = element.table?.rows ?? [];
367
+ const rowsHtml = rows
368
+ .map(
369
+ (row) =>
370
+ `<tr>${row
371
+ .map((cell) =>
372
+ buildFidelityTableCell(cell, widthEmu, refWidthPx, themeFont),
373
+ )
374
+ .join("")}</tr>`,
375
+ )
376
+ .join("");
377
+ return `<div class="fmd-pptx-table" data-pptx-element-kind="table"${objectId} style="${position}${rotation} overflow: hidden;"><table style="width:100%;height:100%;border-collapse:collapse;table-layout:fixed;font-family:${cssFontFamily(themeFont)};">${rowsHtml}</table></div>`;
378
+ }
379
+
380
+ function buildFidelityTableCell(
381
+ cell: ParsedTableCell,
382
+ widthEmu: number,
383
+ refWidthPx: number,
384
+ themeFont: string | undefined,
385
+ ): string {
386
+ const fill = cell.fill ? `background:${esc(cell.fill)};` : "";
387
+ const paragraphsHtml = cell.paragraphs
388
+ .map((paragraph, paragraphIndex) =>
389
+ buildFidelityParagraph(
390
+ paragraph,
391
+ paragraphIndex,
392
+ widthEmu,
393
+ refWidthPx,
394
+ themeFont,
395
+ 400,
396
+ ),
397
+ )
398
+ .join("");
399
+ return `<td colspan="${cell.colSpan ?? 1}" rowspan="${cell.rowSpan ?? 1}" style="border:1px solid ${DEFAULT_PPTX_TABLE_BORDER};padding:4px 8px;vertical-align:top;${fill}">${paragraphsHtml}</td>`;
400
+ }
401
+
334
402
  function shapeDecoration(
335
403
  element: ParsedElement,
336
404
  widthEmu: number,