@agent-native/core 0.161.7 → 0.161.8

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 (34) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/content/server/plugins/agent-chat.ts +2 -0
  3. package/corpus/templates/content/server/plugins/feature-flags.ts +5 -0
  4. package/corpus/templates/content/shared/feature-flags.ts +15 -0
  5. package/corpus/templates/slides/.agents/skills/slide-editing/SKILL.md +9 -0
  6. package/corpus/templates/slides/actions/patch-deck.ts +8 -0
  7. package/corpus/templates/slides/app/components/editor/EditorSidebar.tsx +188 -66
  8. package/corpus/templates/slides/app/components/presentation/PresentationView.tsx +102 -22
  9. package/corpus/templates/slides/app/components/presentation/PresenterView.tsx +84 -2
  10. package/corpus/templates/slides/app/context/DeckContext.tsx +94 -3
  11. package/corpus/templates/slides/app/i18n/en-US.ts +5 -0
  12. package/corpus/templates/slides/app/pages/DeckEditor.tsx +122 -8
  13. package/corpus/templates/videos/package.json +9 -0
  14. package/dist/a2a/correlation.d.ts +6 -6
  15. package/dist/a2a/correlation.js +8 -6
  16. package/dist/a2a/types.d.ts +7 -5
  17. package/dist/collab/awareness.d.ts +2 -2
  18. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  19. package/dist/observability/routes.d.ts +5 -5
  20. package/dist/provider-api/actions/custom-provider-registration.d.ts +13 -13
  21. package/dist/provider-api/actions/provider-api.d.ts +11 -11
  22. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  23. package/dist/resources/handlers.d.ts +1 -1
  24. package/dist/scripts/call-agent.js +6 -5
  25. package/dist/secrets/routes.d.ts +9 -9
  26. package/dist/server/agent-chat/action-filters-a2a.d.ts +6 -1
  27. package/dist/server/agent-chat/action-filters-a2a.js +29 -2
  28. package/dist/server/agent-chat/plugin-options.d.ts +6 -0
  29. package/dist/server/agent-chat-plugin.d.ts +2 -1
  30. package/dist/server/agent-chat-plugin.js +20 -4
  31. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  32. package/dist/server/realtime-token.d.ts +1 -1
  33. package/dist/server/transcribe-voice.d.ts +1 -1
  34. package/package.json +1 -1
@@ -31,9 +31,32 @@ export default function PresenterView({
31
31
  designSystem,
32
32
  }: PresenterViewProps) {
33
33
  const t = useT();
34
- const [index, setIndex] = useState(startIndex);
34
+ // `startIndex` is a raw index into the full (unfiltered) deck.slides array.
35
+ // Skipped slides are absent from safeSlides below, so translate it to the
36
+ // nearest visible slide's position within safeSlides — matching
37
+ // PresentationView, whose filtered currentIndex this view's `index` state
38
+ // otherwise mirrors via the BroadcastChannel.
39
+ const initialIndex = useMemo(() => {
40
+ const rawSlides = (Array.isArray(slides) ? slides : []).filter(Boolean);
41
+ if (rawSlides.length === 0) return 0;
42
+ const clampedRaw = Math.max(0, Math.min(startIndex, rawSlides.length - 1));
43
+ for (let i = clampedRaw; i < rawSlides.length; i++) {
44
+ if (!rawSlides[i]?.skipped) {
45
+ return rawSlides.slice(0, i).filter((s) => !s?.skipped).length;
46
+ }
47
+ }
48
+ for (let i = clampedRaw - 1; i >= 0; i--) {
49
+ if (!rawSlides[i]?.skipped) {
50
+ return rawSlides.slice(0, i).filter((s) => !s?.skipped).length;
51
+ }
52
+ }
53
+ return 0;
54
+ }, [slides, startIndex]);
55
+ const [index, setIndex] = useState(initialIndex);
35
56
  const [elapsed, setElapsed] = useState(0);
36
57
  const channelRef = useRef<BroadcastChannel | null>(null);
58
+ const indexRef = useRef(index);
59
+ indexRef.current = index;
37
60
 
38
61
  useEffect(() => {
39
62
  const channel = openPresentChannel(deckId);
@@ -88,13 +111,72 @@ export default function PresenterView({
88
111
  }, [goNext, goPrev]);
89
112
 
90
113
  const safeSlides = useMemo(
91
- () => (Array.isArray(slides) ? slides.filter(Boolean) : []),
114
+ () =>
115
+ Array.isArray(slides)
116
+ ? slides.filter(
117
+ (slide): slide is Slide => Boolean(slide) && !slide.skipped,
118
+ )
119
+ : [],
92
120
  [slides],
93
121
  );
122
+
123
+ // One atomic effect handles both cases so they can't race each other:
124
+ // - A genuine deep link or deck switch (startIndex/deckId changed) reseeds
125
+ // from initialIndex. This route is reused across decks (see the
126
+ // BroadcastChannel effect above, keyed on deckId).
127
+ // - Otherwise, a skip toggle or reorder changed safeSlides without a new
128
+ // deep link. A length-only clamp would silently swap in a different
129
+ // slide at the same index, so follow the previously-shown slide's id to
130
+ // its new position, falling back to a raw clamp only when it's gone.
131
+ const prevDeepLinkKeyRef = useRef({ startIndex, deckId });
132
+ const prevSafeSlideIdsRef = useRef<string[]>(safeSlides.map((s) => s.id));
133
+ useEffect(() => {
134
+ const prevKey = prevDeepLinkKeyRef.current;
135
+ const isDeepLinkChange =
136
+ prevKey.startIndex !== startIndex || prevKey.deckId !== deckId;
137
+ prevDeepLinkKeyRef.current = { startIndex, deckId };
138
+
139
+ if (isDeepLinkChange) {
140
+ prevSafeSlideIdsRef.current = safeSlides.map((s) => s.id);
141
+ setIndex(initialIndex);
142
+ return;
143
+ }
144
+
145
+ // Read the prior ids before overwriting the ref below — the lookup
146
+ // needs the slide order from before this update, not the one it's
147
+ // producing.
148
+ const activeId = prevSafeSlideIdsRef.current[indexRef.current];
149
+ const newIds = safeSlides.map((s) => s.id);
150
+ const followedIndex = activeId ? newIds.indexOf(activeId) : -1;
151
+ prevSafeSlideIdsRef.current = newIds;
152
+ setIndex(
153
+ followedIndex >= 0
154
+ ? followedIndex
155
+ : Math.max(0, Math.min(indexRef.current, safeSlides.length - 1)),
156
+ );
157
+ // eslint-disable-next-line react-hooks/exhaustive-deps
158
+ }, [safeSlides, startIndex, deckId]);
159
+
94
160
  const current = safeSlides[index];
95
161
  const next = safeSlides[index + 1];
96
162
  const notes = current?.notes?.trim();
97
163
 
164
+ if (safeSlides.length === 0) {
165
+ return (
166
+ // guard:allow-raw-color — matches the presenter's dedicated dark surface used throughout this file, not app chrome
167
+ <div className="fixed inset-0 flex items-center justify-center bg-[hsl(240,6%,6%)] text-white">
168
+ <button
169
+ type="button"
170
+ onClick={() => window.close()}
171
+ // guard:allow-raw-color — matches the presenter's dedicated dark surface used throughout this file, not app chrome
172
+ className="cursor-pointer rounded-lg bg-white/10 px-4 py-3 text-sm hover:bg-white/20"
173
+ >
174
+ {t("presentation.noSlides")}
175
+ </button>
176
+ </div>
177
+ );
178
+ }
179
+
98
180
  return (
99
181
  <div className="fixed inset-0 flex flex-col bg-[hsl(240,6%,6%)] text-white">
100
182
  <header className="flex items-center justify-between gap-4 border-b border-white/10 px-5 py-3">
@@ -112,6 +112,8 @@ export interface Slide {
112
112
  animations?: SlideAnimation[];
113
113
  /** @deprecated Use animations instead */
114
114
  splitByParagraph?: boolean;
115
+ /** Excluded from Present/Presenter mode playback, but stays in the deck. */
116
+ skipped?: boolean;
115
117
  }
116
118
 
117
119
  export type AnimationType = "appear" | "fade" | "slide-up" | "zoom";
@@ -237,6 +239,14 @@ interface DeckContextType {
237
239
  ) => void;
238
240
  deleteSlide: (deckId: string, slideId: string) => void;
239
241
  duplicateSlide: (deckId: string, slideId: string) => string | undefined;
242
+ /** Inserts a copy of arbitrary slide data after `afterSlideId`. Used for
243
+ * slide cut/paste, where the original may already be deleted so there is
244
+ * no live slide id left to duplicate from. */
245
+ pasteSlide: (
246
+ deckId: string,
247
+ afterSlideId: string,
248
+ slideFields: Omit<Slide, "id">,
249
+ ) => string | undefined;
240
250
  reorderSlides: (deckId: string, oldIndex: number, newIndex: number) => void;
241
251
  setDeckSlides: (deckId: string, slides: Slide[]) => void;
242
252
  /**
@@ -287,13 +297,54 @@ type DuplicateDeckActionResult = {
287
297
  url?: string;
288
298
  };
289
299
 
300
+ /** Per-slide fields `get-deck` computes for the agent (slide position/hash
301
+ * hints) that aren't part of the client's own `Slide` shape. Left on the
302
+ * fetched deck, they make every server refetch look "changed" relative to
303
+ * the client's slim optimistic copy — see `normalizeActionDeck`. */
304
+ const GET_DECK_ONLY_SLIDE_FIELDS = [
305
+ "slideNumber",
306
+ "zeroBasedIndex",
307
+ "contentHash",
308
+ ] as const;
309
+
310
+ /** Deck-level fields `get-deck` computes for the agent (counts, deep links,
311
+ * the currently-selected slide) that aren't part of the client's own `Deck`
312
+ * shape. See `normalizeActionDeck`. */
313
+ const GET_DECK_ONLY_DECK_FIELDS = [
314
+ "slideCount",
315
+ "slideNumbering",
316
+ "deepLink",
317
+ "selectedSlideId",
318
+ ] as const;
319
+
290
320
  function normalizeActionDeck(value: unknown): Deck | null {
291
321
  if (!value || typeof value !== "object") return null;
292
322
  const deck = value as Partial<Deck>;
293
323
  if (typeof deck.id !== "string") return null;
294
324
 
325
+ const deckRecord = deck as unknown as Record<string, unknown>;
326
+ const cleanedDeck = { ...deckRecord };
327
+ for (const field of GET_DECK_ONLY_DECK_FIELDS) delete cleanedDeck[field];
328
+
329
+ // Strip the same decorative fields from every slide, so a deck fetched from
330
+ // `get-deck` is structurally identical to one built by local mutations —
331
+ // otherwise `deckContentSignature` sees a "change" on every refetch of the
332
+ // open deck and spams the undo stack with no-op `replace-deck` entries.
333
+ const slides = Array.isArray(deck.slides)
334
+ ? deck.slides.map((slide) => {
335
+ if (!slide || typeof slide !== "object") return slide;
336
+ const cleanedSlide = {
337
+ ...(slide as unknown as Record<string, unknown>),
338
+ };
339
+ for (const field of GET_DECK_ONLY_SLIDE_FIELDS) {
340
+ delete cleanedSlide[field];
341
+ }
342
+ return cleanedSlide as unknown as Slide;
343
+ })
344
+ : [];
345
+
295
346
  return {
296
- ...deck,
347
+ ...cleanedDeck,
297
348
  id: deck.id,
298
349
  title: typeof deck.title === "string" ? deck.title : "Untitled",
299
350
  createdAt:
@@ -304,7 +355,7 @@ function normalizeActionDeck(value: unknown): Deck | null {
304
355
  typeof deck.updatedAt === "string"
305
356
  ? deck.updatedAt
306
357
  : deck.createdAt || "",
307
- slides: Array.isArray(deck.slides) ? deck.slides : [],
358
+ slides,
308
359
  } as Deck;
309
360
  }
310
361
 
@@ -873,7 +924,14 @@ export function deriveInverseOp(
873
924
  // restores exactly what changed (including clearing fields back to
874
925
  // undefined).
875
926
  if (!equalDeckValue(prior[key], op.fields[key])) {
876
- (priorFields as Record<string, unknown>)[key] = prior[key];
927
+ let priorValue: unknown = prior[key];
928
+ // `skipped` is undefined on a slide that was never skipped, but
929
+ // `undefined` doesn't survive JSON transport to the server — its
930
+ // `patch-slide` handler treats an absent field as "don't touch",
931
+ // so the persisted deck would stay skipped after undo. `false` is
932
+ // equivalent for this boolean field and does survive.
933
+ if (key === "skipped" && priorValue === undefined) priorValue = false;
934
+ (priorFields as Record<string, unknown>)[key] = priorValue;
877
935
  }
878
936
  }
879
937
  if (Object.keys(priorFields).length === 0) return null;
@@ -2346,6 +2404,38 @@ export function DeckProvider({ children }: { children: ReactNode }) {
2346
2404
  [markDeckDirty, recordUndo, setDecksLocal],
2347
2405
  );
2348
2406
 
2407
+ const pasteSlide = useCallback(
2408
+ (deckId: string, afterSlideId: string, slideFields: Omit<Slide, "id">) => {
2409
+ const before = decksRef.current.find((d) => d.id === deckId);
2410
+ if (!before) return undefined;
2411
+
2412
+ markDeckDirty(deckId);
2413
+ const newSlide: Slide = { ...slideFields, id: nanoid(8) };
2414
+ setDecksLocal((prev) =>
2415
+ prev.map((d) => {
2416
+ if (d.id !== deckId) return d;
2417
+ const idx = d.slides.findIndex((s) => s.id === afterSlideId);
2418
+ const insertAt = idx === -1 ? d.slides.length : idx + 1;
2419
+ const slides = [...d.slides];
2420
+ slides.splice(insertAt, 0, newSlide);
2421
+ return { ...d, slides, updatedAt: new Date().toISOString() };
2422
+ }),
2423
+ );
2424
+ // Granular add-slide op, same as duplicateSlide — inserts after
2425
+ // afterSlideId regardless of whether that id is also the copy source.
2426
+ const op: PatchDeckOp = {
2427
+ op: "add-slide",
2428
+ slideId: newSlide.id,
2429
+ afterSlideId,
2430
+ fields: addSlideFields(newSlide),
2431
+ };
2432
+ enqueueDeckOp(deckId, op);
2433
+ recordUndo(before, op, { label: "Paste slide" });
2434
+ return newSlide.id;
2435
+ },
2436
+ [markDeckDirty, recordUndo, setDecksLocal],
2437
+ );
2438
+
2349
2439
  const reorderSlides = useCallback(
2350
2440
  (deckId: string, oldIndex: number, newIndex: number) => {
2351
2441
  markDeckDirty(deckId);
@@ -2432,6 +2522,7 @@ export function DeckProvider({ children }: { children: ReactNode }) {
2432
2522
  updateSlide,
2433
2523
  deleteSlide,
2434
2524
  duplicateSlide,
2525
+ pasteSlide,
2435
2526
  reorderSlides,
2436
2527
  setDeckSlides,
2437
2528
  markDeckDirty,
@@ -524,6 +524,11 @@ const messages = {
524
524
  newSlide: "New slide",
525
525
  closeAddSlides: "Close",
526
526
  describeThisSlide: "Describe this slide",
527
+ cut: "Cut",
528
+ copy: "Copy",
529
+ paste: "Paste",
530
+ skipSlide: "Skip slide",
531
+ unskipSlide: "Unskip slide",
527
532
  },
528
533
  presentation: {
529
534
  loadFailed: "Could not load this presentation.",
@@ -60,6 +60,7 @@ import { Button } from "@/components/ui/button";
60
60
  import {
61
61
  deckIdFromPathname,
62
62
  hasUnsavedDeckChanges,
63
+ type Slide,
63
64
  useDecks,
64
65
  useSaveState,
65
66
  } from "@/context/DeckContext";
@@ -185,6 +186,7 @@ export default function DeckEditor() {
185
186
  updateSlide,
186
187
  deleteSlide,
187
188
  duplicateSlide,
189
+ pasteSlide,
188
190
  duplicateDeck,
189
191
  addSlide,
190
192
  flushDeckSave,
@@ -852,11 +854,107 @@ export default function DeckEditor() {
852
854
  return () => document.removeEventListener("keydown", handleKeyDown);
853
855
  }, [deck, id, activeSlideId, deleteSlideWithUndo, pinMode, drawMode]);
854
856
 
855
- // Command/Ctrl+C then Command/Ctrl+V on the slide rail duplicates the
857
+ // Slide-level clipboard backing both the Cmd+C/Cmd+V shortcut below and the
858
+ // rail's right-click Cut/Copy/Paste menu. Holds a full slide snapshot
859
+ // (rather than just an id) so paste still works after Cut has already
860
+ // removed the original slide from the deck.
861
+ const slideClipboardRef = useRef<Slide | null>(null);
862
+ const [hasSlideClipboard, setHasSlideClipboard] = useState(false);
863
+
864
+ const copySlide = useCallback(
865
+ (slideId: string) => {
866
+ const slide = deck?.slides.find((s) => s.id === slideId);
867
+ if (!slide) return;
868
+ slideClipboardRef.current = slide;
869
+ setHasSlideClipboard(true);
870
+ },
871
+ [deck],
872
+ );
873
+
874
+ const cutSlide = useCallback(
875
+ (slideId: string) => {
876
+ if (!deck || !id || deck.slides.length <= 1) return; // don't cut the last slide
877
+ const slide = deck.slides.find((s) => s.id === slideId);
878
+ if (!slide) return;
879
+ slideClipboardRef.current = slide;
880
+ setHasSlideClipboard(true);
881
+ const idx = deck.slides.findIndex((s) => s.id === slideId);
882
+ const nextSlide = deck.slides[idx + 1] || deck.slides[idx - 1];
883
+ deleteSlideWithUndo(id, slideId);
884
+ if (activeSlideId === slideId && nextSlide) {
885
+ setActiveSlideId(nextSlide.id);
886
+ }
887
+ },
888
+ [deck, id, activeSlideId, deleteSlideWithUndo],
889
+ );
890
+
891
+ const pasteSlideAfter = useCallback(
892
+ (targetSlideId: string) => {
893
+ const clipboard = slideClipboardRef.current;
894
+ if (!clipboard || !id) return;
895
+ const { id: _clipboardId, ...fields } = clipboard;
896
+ const newId = pasteSlide(id, targetSlideId, fields);
897
+ if (newId) setActiveSlideId(newId);
898
+ },
899
+ [id, pasteSlide],
900
+ );
901
+
902
+ // Handlers backing the slide rail's right-click menu.
903
+ const handleDeleteSlideFromRail = useCallback(
904
+ (slideId: string) => {
905
+ if (!deck || !id || deck.slides.length <= 1) return; // don't delete the last slide
906
+ const idx = deck.slides.findIndex((s) => s.id === slideId);
907
+ const nextSlide = deck.slides[idx + 1] || deck.slides[idx - 1];
908
+ deleteSlideWithUndo(id, slideId);
909
+ if (activeSlideId === slideId && nextSlide) {
910
+ setActiveSlideId(nextSlide.id);
911
+ }
912
+ },
913
+ [deck, id, activeSlideId, deleteSlideWithUndo],
914
+ );
915
+
916
+ const handleDuplicateSlideFromRail = useCallback(
917
+ (slideId: string) => {
918
+ if (!id) return;
919
+ const newId = duplicateSlide(id, slideId);
920
+ if (newId) setActiveSlideId(newId);
921
+ },
922
+ [id, duplicateSlide],
923
+ );
924
+
925
+ const handleNewSlideAfter = useCallback(
926
+ (afterSlideId: string) => {
927
+ if (!deck || !id) return;
928
+ const afterIdx = deck.slides.findIndex((s) => s.id === afterSlideId);
929
+ // Immediate persistence: mirrors handleAddEmptySlide, since this also
930
+ // opens the "describe this slide" popover right away.
931
+ const newId = addSlide(
932
+ id,
933
+ "blank",
934
+ afterIdx >= 0 ? afterIdx : undefined,
935
+ { persistence: "immediate" },
936
+ );
937
+ setActiveSlideId(newId);
938
+ setSidebarOpen(true);
939
+ setDescribeSlideId(newId);
940
+ },
941
+ [deck, id, addSlide],
942
+ );
943
+
944
+ const handleToggleSkipSlide = useCallback(
945
+ (slideId: string) => {
946
+ if (!deck || !id) return;
947
+ const slide = deck.slides.find((s) => s.id === slideId);
948
+ if (!slide) return;
949
+ updateSlide(id, slideId, { skipped: !slide.skipped });
950
+ },
951
+ [deck, id, updateSlide],
952
+ );
953
+
954
+ // Command/Ctrl+C then Command/Ctrl+V on the slide rail copies/pastes the
856
955
  // selected slide directly below itself. Only claims the shortcut when no
857
956
  // slide element is selected — SlideEditor owns Cmd+C/V for object copy/paste
858
957
  // in that case.
859
- const copiedSlideIdRef = useRef<string | null>(null);
860
958
  useEffect(() => {
861
959
  const handleKeyDown = (e: KeyboardEvent) => {
862
960
  if (!deck || !id || !canEdit) return;
@@ -925,19 +1023,27 @@ export default function DeckEditor() {
925
1023
 
926
1024
  if (key === "c") {
927
1025
  if (!activeSlideId) return;
928
- copiedSlideIdRef.current = activeSlideId;
1026
+ copySlide(activeSlideId);
929
1027
  return;
930
1028
  }
931
1029
 
932
- const copiedId = copiedSlideIdRef.current;
933
- if (!copiedId || !deck.slides.some((s) => s.id === copiedId)) return;
1030
+ if (!hasSlideClipboard || !activeSlideId) return;
934
1031
  e.preventDefault();
935
- const newId = duplicateSlide(id, copiedId);
936
- if (newId) setActiveSlideId(newId);
1032
+ pasteSlideAfter(activeSlideId);
937
1033
  };
938
1034
  document.addEventListener("keydown", handleKeyDown);
939
1035
  return () => document.removeEventListener("keydown", handleKeyDown);
940
- }, [deck, id, canEdit, activeSlideId, duplicateSlide, pinMode, drawMode]);
1036
+ }, [
1037
+ deck,
1038
+ id,
1039
+ canEdit,
1040
+ activeSlideId,
1041
+ copySlide,
1042
+ hasSlideClipboard,
1043
+ pasteSlideAfter,
1044
+ pinMode,
1045
+ drawMode,
1046
+ ]);
941
1047
 
942
1048
  // Resolve the active slide from URL/deck state. Imports replace slide IDs, so
943
1049
  // keep this valid after deck contents change instead of only on first load.
@@ -1371,6 +1477,14 @@ export default function DeckEditor() {
1371
1477
  setGeneratingSlideSelected(true);
1372
1478
  if (window.innerWidth < 768) setSidebarOpen(false);
1373
1479
  }}
1480
+ hasSlideClipboard={hasSlideClipboard}
1481
+ onCutSlide={cutSlide}
1482
+ onCopySlide={copySlide}
1483
+ onPasteSlide={pasteSlideAfter}
1484
+ onDeleteSlide={handleDeleteSlideFromRail}
1485
+ onNewSlideAfter={handleNewSlideAfter}
1486
+ onDuplicateSlide={handleDuplicateSlideFromRail}
1487
+ onToggleSkipSlide={handleToggleSkipSlide}
1374
1488
  />
1375
1489
  </DndContext>
1376
1490
  </div>
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "videos",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "node ../../scripts/build-retired-netlify-site.ts videos"
7
+ },
8
+ "agentNativeRetiredCompatibility": true
9
+ }
@@ -3,11 +3,11 @@ export declare const MAX_A2A_CORRELATION_VALUE_CHARS = 200;
3
3
  export declare const MAX_A2A_DELEGATION_HOPS = 3;
4
4
  export declare function sanitizeA2ACorrelationId(value: unknown): string | undefined;
5
5
  /**
6
- * Keep only bounded, opaque ASCII correlation identifiers. These values
7
- * remain telemetry hints; authentication continues to come exclusively from
8
- * the verified A2A token/request context. `callerModel` is the one value here
9
- * a receiver may act on, and only as a preference — it never reaches identity,
10
- * org, access, or approval resolution, and it can only name a model the
11
- * receiver's own engine already offers (see `resolveDelegatedRunModel`).
6
+ * Keep only bounded, opaque ASCII correlation and routing identifiers.
7
+ * Authentication continues to come exclusively from the verified A2A
8
+ * token/request context. A receiver may use `selectedReceiverApp` only to
9
+ * prioritize its matching local tool surface, and `callerModel` only to choose
10
+ * a model its own engine already offers. Neither reaches identity, data
11
+ * ownership, org, access, or approval resolution.
12
12
  */
13
13
  export declare function sanitizeA2ACorrelationMetadata(value: unknown): A2ACorrelationMetadata;
@@ -19,18 +19,19 @@ export function sanitizeA2ACorrelationId(value) {
19
19
  return boundedIdentifier(value, CORRELATION_ID_PATTERN);
20
20
  }
21
21
  /**
22
- * Keep only bounded, opaque ASCII correlation identifiers. These values
23
- * remain telemetry hints; authentication continues to come exclusively from
24
- * the verified A2A token/request context. `callerModel` is the one value here
25
- * a receiver may act on, and only as a preference — it never reaches identity,
26
- * org, access, or approval resolution, and it can only name a model the
27
- * receiver's own engine already offers (see `resolveDelegatedRunModel`).
22
+ * Keep only bounded, opaque ASCII correlation and routing identifiers.
23
+ * Authentication continues to come exclusively from the verified A2A
24
+ * token/request context. A receiver may use `selectedReceiverApp` only to
25
+ * prioritize its matching local tool surface, and `callerModel` only to choose
26
+ * a model its own engine already offers. Neither reaches identity, data
27
+ * ownership, org, access, or approval resolution.
28
28
  */
29
29
  export function sanitizeA2ACorrelationMetadata(value) {
30
30
  if (!value || typeof value !== "object" || Array.isArray(value))
31
31
  return {};
32
32
  const metadata = value;
33
33
  const callerApp = boundedIdentifier(metadata.callerApp, APP_ID_PATTERN);
34
+ const selectedReceiverApp = boundedIdentifier(metadata.selectedReceiverApp, APP_ID_PATTERN);
34
35
  const callerThreadId = sanitizeA2ACorrelationId(metadata.callerThreadId);
35
36
  const parentRunId = sanitizeA2ACorrelationId(metadata.parentRunId);
36
37
  const parentTurnId = sanitizeA2ACorrelationId(metadata.parentTurnId);
@@ -62,6 +63,7 @@ export function sanitizeA2ACorrelationMetadata(value) {
62
63
  : undefined;
63
64
  return {
64
65
  ...(callerApp ? { callerApp } : {}),
66
+ ...(selectedReceiverApp ? { selectedReceiverApp } : {}),
65
67
  ...(callerThreadId ? { callerThreadId } : {}),
66
68
  ...(parentRunId ? { parentRunId } : {}),
67
69
  ...(parentTurnId ? { parentTurnId } : {}),
@@ -117,14 +117,16 @@ export interface A2ASourceContextReference {
117
117
  integrationTaskId: string;
118
118
  }
119
119
  /**
120
- * Telemetry-only cross-app correlation, plus `callerModel` a preference
121
- * hint. Receivers must never use any caller-supplied value here for identity,
122
- * ownership, org scoping, access, or approval decisions. `callerModel` widens
123
- * this channel to a preference, never to an authorization: it may at most pick
124
- * a model the receiver's already-resolved engine advertises.
120
+ * Bounded cross-app correlation and routing preferences. Receivers must never
121
+ * use any caller-supplied value here for identity, data ownership, org scoping,
122
+ * access, or approval decisions. `selectedReceiverApp` may only prioritize the
123
+ * matching receiver's local tool surface, while `callerModel` may only pick a
124
+ * model the receiver's already-resolved engine advertises.
125
125
  */
126
126
  export interface A2ACorrelationMetadata {
127
127
  callerApp?: string;
128
+ /** App the caller deliberately selected for this delegated objective. */
129
+ selectedReceiverApp?: string;
128
130
  callerThreadId?: string;
129
131
  parentRunId?: string;
130
132
  parentTurnId?: string;
@@ -62,11 +62,11 @@ export declare const postAwareness: import("h3").EventHandlerWithFetch<import("h
62
62
  error: string;
63
63
  states?: undefined;
64
64
  } | {
65
+ error?: undefined;
65
66
  states: {
66
67
  clientId: number;
67
68
  state: string;
68
69
  }[];
69
- error?: undefined;
70
70
  }>>;
71
71
  /**
72
72
  * GET /_agent-native/collab/:docId/users
@@ -77,9 +77,9 @@ export declare const getActiveUsers: import("h3").EventHandlerWithFetch<import("
77
77
  error: string;
78
78
  users?: undefined;
79
79
  } | {
80
+ error?: undefined;
80
81
  users: {
81
82
  clientId: number;
82
83
  lastSeen: number;
83
84
  }[];
84
- error?: undefined;
85
85
  }>>;
@@ -17,11 +17,11 @@ declare const _default: import("../../action.js").ActionDefinition<{
17
17
  id?: undefined;
18
18
  provider?: undefined;
19
19
  } | {
20
+ error?: undefined;
20
21
  configured?: undefined;
21
22
  connectPath?: undefined;
22
23
  url: string;
23
24
  id: string;
24
25
  provider: string;
25
- error?: undefined;
26
26
  }>;
27
27
  export default _default;
@@ -41,27 +41,27 @@ export declare function createObservabilityHandler(): import("h3").EventHandlerW
41
41
  thumbsUpRate: number;
42
42
  avgEvalScore: number;
43
43
  } | {
44
- error?: undefined;
45
- ok?: undefined;
46
44
  summary: import("./types.js").TraceSummary;
47
45
  spans: import("./types.js").TraceSpan[];
48
46
  id?: undefined;
49
- } | {
50
47
  error?: undefined;
51
48
  ok?: undefined;
49
+ } | {
52
50
  summary?: undefined;
53
51
  spans?: undefined;
54
52
  id: string;
55
- } | {
53
+ error?: undefined;
56
54
  ok?: undefined;
55
+ } | {
57
56
  summary?: undefined;
58
57
  spans?: undefined;
59
58
  id?: undefined;
60
59
  error: any;
60
+ ok?: undefined;
61
61
  } | {
62
- error?: undefined;
63
62
  summary?: undefined;
64
63
  spans?: undefined;
65
64
  id?: undefined;
65
+ error?: undefined;
66
66
  ok: boolean;
67
67
  }>>;
@@ -75,8 +75,7 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
75
75
  user: "user";
76
76
  }>>;
77
77
  }, z.core.$strip>>, {
78
- message?: undefined;
79
- id?: undefined;
78
+ found?: undefined;
80
79
  deleted?: undefined;
81
80
  providers: {
82
81
  id: string;
@@ -89,45 +88,46 @@ export declare function createCustomProviderRegistrationAction<TSchema extends Z
89
88
  }[];
90
89
  count: number;
91
90
  provider?: undefined;
92
- found?: undefined;
93
91
  registered?: undefined;
92
+ id?: undefined;
94
93
  label?: undefined;
95
- } | {
96
94
  message?: undefined;
97
- count?: undefined;
98
- id?: undefined;
95
+ } | {
99
96
  deleted?: undefined;
100
97
  providers?: undefined;
98
+ count?: undefined;
101
99
  found: boolean;
102
100
  provider: import("../custom-registry.js").CustomProviderConfig;
103
101
  registered?: undefined;
102
+ id?: undefined;
104
103
  label?: undefined;
105
- } | {
106
104
  message?: undefined;
107
- count?: undefined;
105
+ } | {
108
106
  deleted?: undefined;
109
107
  providers?: undefined;
108
+ count?: undefined;
110
109
  provider?: undefined;
111
110
  found: boolean;
112
111
  id: string;
113
112
  registered?: undefined;
114
113
  label?: undefined;
115
- } | {
116
114
  message?: undefined;
117
- count?: undefined;
115
+ } | {
116
+ found?: undefined;
118
117
  providers?: undefined;
118
+ count?: undefined;
119
119
  provider?: undefined;
120
- found?: undefined;
121
120
  deleted: boolean;
122
121
  id: string;
123
122
  registered?: undefined;
124
123
  label?: undefined;
124
+ message?: undefined;
125
125
  } | {
126
- count?: undefined;
126
+ found?: undefined;
127
127
  deleted?: undefined;
128
128
  providers?: undefined;
129
+ count?: undefined;
129
130
  provider?: undefined;
130
- found?: undefined;
131
131
  registered: boolean;
132
132
  id: string;
133
133
  label: string;