@agent-native/core 0.120.4 → 0.121.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 (66) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +20 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/agent/durable-background.ts +12 -4
  5. package/corpus/core/src/agent/run-manager.ts +3 -0
  6. package/corpus/core/src/client/use-db-sync.ts +23 -21
  7. package/corpus/core/src/deploy/build.ts +1 -0
  8. package/corpus/core/src/server/core-routes-plugin.ts +3 -0
  9. package/corpus/core/src/server/gateway-access-check.ts +67 -0
  10. package/corpus/core/src/server/poll.ts +295 -9
  11. package/corpus/core/src/server/short-lived-token.ts +122 -0
  12. package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +29 -4
  13. package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +365 -9
  14. package/corpus/templates/slides/app/components/editor/bullet-editing.ts +350 -0
  15. package/corpus/templates/slides/app/context/DeckContext.tsx +1 -3
  16. package/corpus/templates/slides/app/i18n/en-US.ts +2 -0
  17. package/corpus/templates/slides/app/pages/DeckEditor.tsx +5 -0
  18. package/corpus/templates/slides/changelog/2026-07-23-new-bullet-rows-created-with-enter-now-keep-the-list-item-s-.md +6 -0
  19. package/corpus/templates/slides/changelog/2026-07-23-pressing-enter-in-generated-checkbox-shape-marker-lists-now-.md +6 -0
  20. package/corpus/templates/slides/changelog/2026-07-24-typing-a-markdown-style-dash-space-at-the-start-of-a-text-bl.md +6 -0
  21. package/dist/agent/durable-background.d.ts.map +1 -1
  22. package/dist/agent/durable-background.js +16 -4
  23. package/dist/agent/durable-background.js.map +1 -1
  24. package/dist/agent/run-manager.d.ts.map +1 -1
  25. package/dist/agent/run-manager.js +6 -0
  26. package/dist/agent/run-manager.js.map +1 -1
  27. package/dist/client/use-db-sync.d.ts.map +1 -1
  28. package/dist/client/use-db-sync.js +27 -22
  29. package/dist/client/use-db-sync.js.map +1 -1
  30. package/dist/collab/awareness.d.ts +2 -2
  31. package/dist/collab/awareness.d.ts.map +1 -1
  32. package/dist/deploy/build.d.ts.map +1 -1
  33. package/dist/deploy/build.js +1 -0
  34. package/dist/deploy/build.js.map +1 -1
  35. package/dist/observability/routes.d.ts +3 -3
  36. package/dist/progress/routes.d.ts +1 -1
  37. package/dist/provider-api/actions/custom-provider-registration.d.ts +4 -4
  38. package/dist/provider-api/actions/provider-api.d.ts +6 -6
  39. package/dist/resources/handlers.d.ts +1 -1
  40. package/dist/secrets/routes.d.ts +9 -9
  41. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  42. package/dist/server/core-routes-plugin.js +3 -0
  43. package/dist/server/core-routes-plugin.js.map +1 -1
  44. package/dist/server/gateway-access-check.d.ts +12 -0
  45. package/dist/server/gateway-access-check.d.ts.map +1 -0
  46. package/dist/server/gateway-access-check.js +49 -0
  47. package/dist/server/gateway-access-check.js.map +1 -0
  48. package/dist/server/poll.d.ts +55 -1
  49. package/dist/server/poll.d.ts.map +1 -1
  50. package/dist/server/poll.js +254 -7
  51. package/dist/server/poll.js.map +1 -1
  52. package/dist/server/realtime-token.d.ts +1 -1
  53. package/dist/server/short-lived-token.d.ts +28 -0
  54. package/dist/server/short-lived-token.d.ts.map +1 -1
  55. package/dist/server/short-lived-token.js +78 -0
  56. package/dist/server/short-lived-token.js.map +1 -1
  57. package/dist/server/transcribe-voice.d.ts +1 -1
  58. package/package.json +1 -1
  59. package/src/agent/durable-background.ts +12 -4
  60. package/src/agent/run-manager.ts +3 -0
  61. package/src/client/use-db-sync.ts +23 -21
  62. package/src/deploy/build.ts +1 -0
  63. package/src/server/core-routes-plugin.ts +3 -0
  64. package/src/server/gateway-access-check.ts +67 -0
  65. package/src/server/poll.ts +295 -9
  66. package/src/server/short-lived-token.ts +122 -0
@@ -324,3 +324,125 @@ export function verifyRealtimeSubscribeToken(
324
324
  exp: claims.exp,
325
325
  };
326
326
  }
327
+
328
+ // ── Gateway access-check tokens ──────────────────────────────────────────────
329
+ //
330
+ // The hosted gateway has no access to an app's shareable-resource registry, so
331
+ // it cannot resolve sharee visibility itself. It signs one of these with the
332
+ // app's per-project key and calls the app's `/_agent-native/can-see`, which runs
333
+ // `resolveAccess` and answers. The full access query is bound into the token so
334
+ // the app authenticates the params, not merely the caller.
335
+
336
+ /** Payload `typ` discriminator for gateway access-check tokens. */
337
+ export const GATEWAY_ACCESS_TOKEN_TYPE = "rt-access-check";
338
+ /** Short TTL: minted per check, used immediately server-to-server. */
339
+ const DEFAULT_GATEWAY_ACCESS_TTL_SECONDS = 60;
340
+
341
+ /** Inputs for {@link signGatewayAccessToken}. */
342
+ export interface GatewayAccessClaims {
343
+ projectId: string;
344
+ resourceType: string;
345
+ resourceId: string;
346
+ /** App end-user whose visibility of the resource is being checked. */
347
+ userEmail: string;
348
+ orgId?: string;
349
+ ttlSeconds?: number;
350
+ }
351
+
352
+ interface DecodedGatewayAccessClaims {
353
+ typ: string;
354
+ projectId: string;
355
+ resourceType: string;
356
+ resourceId: string;
357
+ userEmail: string;
358
+ orgId?: string;
359
+ exp: number;
360
+ }
361
+
362
+ /** Result of {@link verifyGatewayAccessToken}; on success carries the bound query. */
363
+ export type GatewayAccessVerifyResult =
364
+ | {
365
+ ok: true;
366
+ projectId: string;
367
+ resourceType: string;
368
+ resourceId: string;
369
+ userEmail: string;
370
+ orgId?: string;
371
+ }
372
+ | { ok: false; reason: string };
373
+
374
+ /** Mint a gateway access-check token, signed with the app's per-project `key`. */
375
+ export function signGatewayAccessToken(
376
+ claims: GatewayAccessClaims,
377
+ key: string,
378
+ ): string {
379
+ if (!key) throw new Error("signGatewayAccessToken requires a key");
380
+ const ttl = claims.ttlSeconds ?? DEFAULT_GATEWAY_ACCESS_TTL_SECONDS;
381
+ const payload: DecodedGatewayAccessClaims = {
382
+ typ: GATEWAY_ACCESS_TOKEN_TYPE,
383
+ projectId: claims.projectId,
384
+ resourceType: claims.resourceType,
385
+ resourceId: claims.resourceId,
386
+ userEmail: claims.userEmail,
387
+ exp: Math.floor(Date.now() / 1000) + ttl,
388
+ };
389
+ if (claims.orgId) payload.orgId = claims.orgId;
390
+ const payloadStr = base64UrlEncode(JSON.stringify(payload));
391
+ return `${payloadStr}.${hmacB64(payloadStr, key)}`;
392
+ }
393
+
394
+ /** Verify a gateway access-check token against the app's per-project `key`. */
395
+ export function verifyGatewayAccessToken(
396
+ token: string,
397
+ key: string,
398
+ expectedProjectId?: string,
399
+ ): GatewayAccessVerifyResult {
400
+ if (!key) return { ok: false, reason: "no_key" };
401
+ if (typeof token !== "string" || !token.includes(".")) {
402
+ return { ok: false, reason: "malformed" };
403
+ }
404
+ const [payloadStr, sig] = token.split(".", 2);
405
+ if (!payloadStr || !sig) return { ok: false, reason: "malformed" };
406
+
407
+ if (!timingSafeEqualB64(sig, hmacB64(payloadStr, key))) {
408
+ return { ok: false, reason: "bad_signature" };
409
+ }
410
+
411
+ let claims: DecodedGatewayAccessClaims;
412
+ try {
413
+ claims = JSON.parse(base64UrlDecode(payloadStr).toString("utf8"));
414
+ } catch {
415
+ return { ok: false, reason: "bad_payload" };
416
+ }
417
+
418
+ if (claims.typ !== GATEWAY_ACCESS_TOKEN_TYPE) {
419
+ return { ok: false, reason: "wrong_type" };
420
+ }
421
+ if (typeof claims.exp !== "number")
422
+ return { ok: false, reason: "bad_payload" };
423
+ if (claims.exp * 1000 < Date.now()) return { ok: false, reason: "expired" };
424
+ if (
425
+ !claims.projectId ||
426
+ !claims.resourceType ||
427
+ !claims.resourceId ||
428
+ !claims.userEmail
429
+ ) {
430
+ return { ok: false, reason: "bad_payload" };
431
+ }
432
+ // Optional channel binding, mirroring verifyRealtimeSubscribeToken. The
433
+ // per-project key already scopes verification to one app; this is belt-and-
434
+ // suspenders for a future multi-tenant secret store. Skipped when the caller
435
+ // can't cheaply resolve its own project id (scoped-secret apps).
436
+ if (expectedProjectId && claims.projectId !== expectedProjectId) {
437
+ return { ok: false, reason: "wrong_project" };
438
+ }
439
+
440
+ return {
441
+ ok: true,
442
+ projectId: claims.projectId,
443
+ resourceType: claims.resourceType,
444
+ resourceId: claims.resourceId,
445
+ userEmail: claims.userEmail,
446
+ orgId: claims.orgId,
447
+ };
448
+ }
@@ -29,6 +29,7 @@ import {
29
29
  IconAdjustments,
30
30
  IconPencilPlus,
31
31
  IconPin,
32
+ IconLetterT,
32
33
  IconWand,
33
34
  IconUpload,
34
35
  IconSun,
@@ -123,6 +124,10 @@ interface EditorToolbarProps {
123
124
  pinMode?: boolean;
124
125
  /** Toggle comment-pin drop mode */
125
126
  onTogglePinMode?: () => void;
127
+ /** Whether the add-text-box tool is active */
128
+ textBoxMode?: boolean;
129
+ /** Toggle the add-text-box tool */
130
+ onToggleTextBoxMode?: () => void;
126
131
  /** Duplicate the current deck */
127
132
  onDuplicateDeck?: () => void;
128
133
  /** Export the deck as PDF */
@@ -247,6 +252,8 @@ export default function EditorToolbar({
247
252
  onToggleDrawMode,
248
253
  pinMode,
249
254
  onTogglePinMode,
255
+ textBoxMode,
256
+ onToggleTextBoxMode,
250
257
  onDuplicateDeck,
251
258
  onExportPdf,
252
259
  onExportPptx,
@@ -305,10 +312,10 @@ export default function EditorToolbar({
305
312
  const [themeMounted, setThemeMounted] = useState(false);
306
313
  useEffect(() => setThemeMounted(true), []);
307
314
  const isDark = themeMounted ? resolvedTheme === "dark" : false;
308
- // The four secondary tools share an "active when something is on" indicator
309
- // so the dot on the consolidated button reflects any of them.
315
+ // The secondary tools share an "active when something is on" indicator so
316
+ // the dot on the consolidated button reflects any of them.
310
317
  const anyToolActive = Boolean(
311
- animationsOpen || tweaksOpen || drawMode || pinMode,
318
+ animationsOpen || tweaksOpen || drawMode || pinMode || textBoxMode,
312
319
  );
313
320
 
314
321
  const closeAll = () => {
@@ -731,7 +738,8 @@ graph TD
731
738
  (onToggleAnimations ||
732
739
  onToggleTweaks ||
733
740
  onToggleDrawMode ||
734
- onTogglePinMode) && (
741
+ onTogglePinMode ||
742
+ onToggleTextBoxMode) && (
735
743
  <>
736
744
  <Tooltip>
737
745
  <TooltipTrigger asChild>
@@ -834,6 +842,23 @@ graph TD
834
842
  </span>
835
843
  </button>
836
844
  )}
845
+ {onToggleTextBoxMode && (
846
+ <button
847
+ onClick={() => {
848
+ onToggleTextBoxMode();
849
+ setToolsOpen(false);
850
+ }}
851
+ data-toolbar-textbox-button
852
+ className={`flex items-center gap-2 w-full px-3 py-1.5 text-xs transition-colors ${
853
+ textBoxMode
854
+ ? "text-foreground bg-accent/50"
855
+ : "text-muted-foreground hover:text-foreground hover:bg-accent/50"
856
+ }`}
857
+ >
858
+ <IconLetterT className="w-3.5 h-3.5" />
859
+ {t("editorToolbar.addTextBox")}
860
+ </button>
861
+ )}
837
862
  </div>
838
863
  </ToolbarPopover>
839
864
  </>