@agent-native/core 0.109.2 → 0.109.3

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 (97) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +8 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/cli/create.ts +18 -12
  5. package/corpus/core/src/client/require-session.tsx +19 -3
  6. package/corpus/core/src/client/use-action.ts +145 -2
  7. package/corpus/core/src/client/use-session.ts +79 -48
  8. package/corpus/core/src/db/client.ts +13 -7
  9. package/corpus/core/src/db/create-get-db.ts +20 -13
  10. package/corpus/core/src/db/request-telemetry.ts +154 -0
  11. package/corpus/core/src/integrations/index.ts +1 -0
  12. package/corpus/core/src/integrations/plugin.ts +9 -3
  13. package/corpus/core/src/integrations/remote-push-delivery-job.ts +48 -0
  14. package/corpus/core/src/integrations/remote-push-delivery.ts +370 -0
  15. package/corpus/core/src/integrations/remote-push-store.ts +275 -26
  16. package/corpus/core/src/server/better-auth-instance.ts +11 -3
  17. package/corpus/core/src/server/core-routes-plugin.ts +36 -14
  18. package/corpus/core/src/server/framework-request-handler.ts +11 -1
  19. package/corpus/core/src/server/http-response-telemetry.ts +233 -31
  20. package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
  21. package/corpus/templates/clips/actions/finalize-recording.ts +3 -1
  22. package/corpus/templates/clips/actions/set-mobile-capture-state.ts +4 -1
  23. package/corpus/templates/clips/app/components/library/library-layout.tsx +1 -6
  24. package/corpus/templates/clips/changelog/2026-07-18-new-recording-is-now-emphasized-as-the-primary-action-in-the.md +6 -0
  25. package/corpus/templates/clips/changelog/2026-07-18-recordings-now-remain-safely-recoverable-until-the-stored-me.md +6 -0
  26. package/corpus/templates/tasks/AGENTS.md +3 -0
  27. package/corpus/templates/tasks/actions/render-task-list-inline.ts +202 -0
  28. package/corpus/templates/tasks/changelog/2026-07-18-task-lists-can-now-appear-as-interactive-widgets-inline-in-c.md +6 -0
  29. package/corpus/templates/tasks/docs/features/f8-chat.mdx +2 -0
  30. package/corpus/templates/tasks/server/plugins/agent-chat.ts +2 -0
  31. package/dist/cli/create.d.ts +10 -1
  32. package/dist/cli/create.d.ts.map +1 -1
  33. package/dist/cli/create.js +17 -13
  34. package/dist/cli/create.js.map +1 -1
  35. package/dist/client/require-session.d.ts.map +1 -1
  36. package/dist/client/require-session.js +6 -3
  37. package/dist/client/require-session.js.map +1 -1
  38. package/dist/client/use-action.d.ts.map +1 -1
  39. package/dist/client/use-action.js +117 -1
  40. package/dist/client/use-action.js.map +1 -1
  41. package/dist/client/use-session.d.ts.map +1 -1
  42. package/dist/client/use-session.js +72 -50
  43. package/dist/client/use-session.js.map +1 -1
  44. package/dist/collab/routes.d.ts +1 -1
  45. package/dist/collab/struct-routes.d.ts +1 -1
  46. package/dist/db/client.d.ts.map +1 -1
  47. package/dist/db/client.js +7 -1
  48. package/dist/db/client.js.map +1 -1
  49. package/dist/db/create-get-db.d.ts +3 -6
  50. package/dist/db/create-get-db.d.ts.map +1 -1
  51. package/dist/db/create-get-db.js +9 -2
  52. package/dist/db/create-get-db.js.map +1 -1
  53. package/dist/db/request-telemetry.d.ts +23 -0
  54. package/dist/db/request-telemetry.d.ts.map +1 -0
  55. package/dist/db/request-telemetry.js +95 -0
  56. package/dist/db/request-telemetry.js.map +1 -0
  57. package/dist/file-upload/actions/upload-image.d.ts +1 -1
  58. package/dist/integrations/index.d.ts +1 -0
  59. package/dist/integrations/index.d.ts.map +1 -1
  60. package/dist/integrations/index.js +1 -0
  61. package/dist/integrations/index.js.map +1 -1
  62. package/dist/integrations/plugin.d.ts.map +1 -1
  63. package/dist/integrations/plugin.js +8 -3
  64. package/dist/integrations/plugin.js.map +1 -1
  65. package/dist/integrations/remote-push-delivery-job.d.ts +4 -0
  66. package/dist/integrations/remote-push-delivery-job.d.ts.map +1 -0
  67. package/dist/integrations/remote-push-delivery-job.js +44 -0
  68. package/dist/integrations/remote-push-delivery-job.js.map +1 -0
  69. package/dist/integrations/remote-push-delivery.d.ts +11 -0
  70. package/dist/integrations/remote-push-delivery.d.ts.map +1 -0
  71. package/dist/integrations/remote-push-delivery.js +265 -0
  72. package/dist/integrations/remote-push-delivery.js.map +1 -0
  73. package/dist/integrations/remote-push-store.d.ts +33 -0
  74. package/dist/integrations/remote-push-store.d.ts.map +1 -1
  75. package/dist/integrations/remote-push-store.js +195 -26
  76. package/dist/integrations/remote-push-store.js.map +1 -1
  77. package/dist/progress/routes.d.ts +1 -1
  78. package/dist/provider-api/corpus-jobs.d.ts +2 -2
  79. package/dist/resources/handlers.d.ts +3 -3
  80. package/dist/secrets/routes.d.ts +9 -9
  81. package/dist/server/better-auth-instance.js +7 -3
  82. package/dist/server/better-auth-instance.js.map +1 -1
  83. package/dist/server/core-routes-plugin.d.ts +8 -0
  84. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  85. package/dist/server/core-routes-plugin.js +23 -8
  86. package/dist/server/core-routes-plugin.js.map +1 -1
  87. package/dist/server/framework-request-handler.d.ts.map +1 -1
  88. package/dist/server/framework-request-handler.js +9 -1
  89. package/dist/server/framework-request-handler.js.map +1 -1
  90. package/dist/server/http-response-telemetry.d.ts +3 -2
  91. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  92. package/dist/server/http-response-telemetry.js +160 -27
  93. package/dist/server/http-response-telemetry.js.map +1 -1
  94. package/dist/server/transcribe-voice.d.ts +1 -1
  95. package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
  96. package/package.json +1 -1
  97. package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +19 -0
@@ -1 +1 @@
1
- {"version":3,"file":"require-session.d.ts","sourceRoot":"","sources":["../../src/client/require-session.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAA4B,MAAM,OAAO,CAAC;AAMjD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAGxC;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,EACR,QAAe,EACf,SAAS,EACT,MAAc,GACf,EAAE,mBAAmB,qBA6BrB"}
1
+ {"version":3,"file":"require-session.d.ts","sourceRoot":"","sources":["../../src/client/require-session.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,KAA4B,MAAM,OAAO,CAAC;AAMjD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,4EAA4E;AAC5E,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,IAAI,OAAO,CAGxC;AAED,wBAAgB,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,EACR,QAAe,EACf,SAAS,EACT,MAAc,GACf,EAAE,mBAAmB,qBAWrB"}
@@ -59,12 +59,17 @@ export function isOnSignInPage() {
59
59
  return window.location.pathname === agentNativePath("/_agent-native/sign-in");
60
60
  }
61
61
  export function RequireSession({ children, fallback, redirect = true, signedOut, bypass = false, }) {
62
+ if (bypass)
63
+ return _jsx(_Fragment, { children: children });
64
+ return (_jsx(ResolvedSessionGate, { fallback: fallback, redirect: redirect, signedOut: signedOut, children: children }));
65
+ }
66
+ function ResolvedSessionGate({ children, fallback, redirect = true, signedOut, }) {
62
67
  const { session, isLoading } = useSession();
63
68
  // Guard against firing the redirect more than once (effect re-runs, React
64
69
  // StrictMode double-invoke) — a second navigation while the first is in
65
70
  // flight is harmless but noisy.
66
71
  const redirectedRef = useRef(false);
67
- const mustRedirect = !bypass && !isLoading && !session && redirect && !isOnSignInPage();
72
+ const mustRedirect = !isLoading && !session && redirect && !isOnSignInPage();
68
73
  useEffect(() => {
69
74
  if (!mustRedirect)
70
75
  return;
@@ -77,8 +82,6 @@ export function RequireSession({ children, fallback, redirect = true, signedOut,
77
82
  // history — pressing Back after signing in shouldn't bounce here again.
78
83
  window.location.replace(buildSignInReturnHref());
79
84
  }, [mustRedirect]);
80
- if (bypass)
81
- return _jsx(_Fragment, { children: children });
82
85
  // Still resolving, or redirect already in flight: show the loading fallback
83
86
  // rather than flashing app chrome the visitor can't use.
84
87
  if (isLoading)
@@ -1 +1 @@
1
- {"version":3,"file":"require-session.js","sourceRoot":"","sources":["../../src/client/require-session.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA8B9C,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,eAAe,CAAC,wBAAwB,CAAC,CAAC;IACvD,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,GAAG,GACP,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC3E,OAAO,GAAG,IAAI,WAAW,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,KAAK,eAAe,CAAC,wBAAwB,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,EACR,QAAQ,GAAG,IAAI,EACf,SAAS,EACT,MAAM,GAAG,KAAK,GACM;IACpB,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC;IAC5C,0EAA0E;IAC1E,wEAAwE;IACxE,gCAAgC;IAChC,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpC,MAAM,YAAY,GAChB,CAAC,MAAM,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,IAAI,QAAQ,IAAI,CAAC,cAAc,EAAE,CAAC;IAErE,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,IAAI,aAAa,CAAC,OAAO;YAAE,OAAO;QAClC,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7B,yEAAyE;QACzE,wEAAwE;QACxE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACnD,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,IAAI,MAAM;QAAE,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACnC,4EAA4E;IAC5E,yDAAyD;IACzD,IAAI,SAAS;QAAE,OAAO,4BAAG,QAAQ,IAAI,KAAC,cAAc,KAAG,GAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,QAAQ;YAAE,OAAO,4BAAG,QAAQ,IAAI,KAAC,cAAc,KAAG,GAAI,CAAC;QAC3D,OAAO,4BAAG,SAAS,IAAI,IAAI,GAAI,CAAC;IAClC,CAAC;IACD,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC","sourcesContent":["/**\n * Client-side session gate for an authenticated app shell.\n *\n * Wrap a template's private app shell with <RequireSession> so a logged-out\n * visitor is sent to the framework sign-in page instead of being left staring\n * at an infinite loading spinner.\n *\n * Why this exists in addition to the server-side auth guard (`runAuthGuard`,\n * which serves the onboarding/sign-in HTML for unauthenticated requests):\n * the server guard only protects requests that actually reach the Nitro\n * function. A statically-served / CDN-cached SPA shell, or a client-side\n * (React Router) navigation made after the session expired, never re-hits the\n * guard — so the app boots with no session, every data query 401s, and the UI\n * sticks on its loading state forever. This component closes that gap by\n * resolving the session on the client and redirecting when there is none.\n *\n * Place it INSIDE your providers (so the fallback is themed) but AROUND the\n * routed app layout:\n *\n * <AppProviders ...>\n * <RequireSession>\n * <AppLayout><Outlet /></AppLayout>\n * </RequireSession>\n * </AppProviders>\n *\n * Templates with public/anonymous routes (share pages, embeds) must NOT wrap\n * their whole app — gate only the private subtree, or pass `bypass` for the\n * surfaces that authenticate by another mechanism.\n */\nimport React, { useEffect, useRef } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\nimport { DefaultSpinner } from \"./DefaultSpinner.js\";\nimport { useSession } from \"./use-session.js\";\n\nexport interface RequireSessionProps {\n children: React.ReactNode;\n /**\n * Rendered while the session is being resolved and while a redirect is in\n * flight. Defaults to the framework `<DefaultSpinner />`.\n */\n fallback?: React.ReactNode;\n /**\n * When true (default), unauthenticated visitors are redirected to the\n * framework sign-in entry point (`/_agent-native/sign-in`) with a `return`\n * query pointing back at the current URL — so they land back here once\n * signed in. When false, `signedOut` is rendered instead and no navigation\n * happens.\n */\n redirect?: boolean;\n /**\n * Rendered for unauthenticated visitors when `redirect` is false. Ignored\n * when `redirect` is true.\n */\n signedOut?: React.ReactNode;\n /**\n * Skip the gate entirely and always render children. Use for surfaces that\n * authenticate by another mechanism (e.g. an embed/popout iframe carrying\n * its own token) so they are never bounced to the sign-in page.\n */\n bypass?: boolean;\n}\n\n/** Build the framework sign-in URL that returns to the current location. */\nexport function buildSignInReturnHref(): string {\n const base = agentNativePath(\"/_agent-native/sign-in\");\n if (typeof window === \"undefined\") return base;\n const ret =\n window.location.pathname + window.location.search + window.location.hash;\n return `${base}?return=${encodeURIComponent(ret)}`;\n}\n\n/**\n * True when the browser is already sitting on the framework sign-in entry\n * point. Redirecting to sign-in from here would append the current sign-in\n * URL as a fresh `?return=`, re-encode it, and navigate again — an infinite\n * loop of ever-growing `…sign-in?return=%252F…sign-in%253Freturn%253D…` URLs.\n *\n * This happens when the authenticated app shell (wrapped in `RequireSession`)\n * is served at the sign-in path instead of the framework login HTML — e.g.\n * under a base-path deploy where the SPA shell answers `/<app>/_agent-native/\n * sign-in`. The sign-in page is the framework's job, not the gate's: never\n * redirect to ourselves. Compared against `agentNativePath(...)` so it matches\n * whether or not the app is mounted under a base path.\n */\nexport function isOnSignInPage(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.location.pathname === agentNativePath(\"/_agent-native/sign-in\");\n}\n\nexport function RequireSession({\n children,\n fallback,\n redirect = true,\n signedOut,\n bypass = false,\n}: RequireSessionProps) {\n const { session, isLoading } = useSession();\n // Guard against firing the redirect more than once (effect re-runs, React\n // StrictMode double-invoke) — a second navigation while the first is in\n // flight is harmless but noisy.\n const redirectedRef = useRef(false);\n\n const mustRedirect =\n !bypass && !isLoading && !session && redirect && !isOnSignInPage();\n\n useEffect(() => {\n if (!mustRedirect) return;\n if (redirectedRef.current) return;\n if (typeof window === \"undefined\") return;\n redirectedRef.current = true;\n // `replace` (not `assign`) so the dead authenticated URL doesn't land in\n // history — pressing Back after signing in shouldn't bounce here again.\n window.location.replace(buildSignInReturnHref());\n }, [mustRedirect]);\n\n if (bypass) return <>{children}</>;\n // Still resolving, or redirect already in flight: show the loading fallback\n // rather than flashing app chrome the visitor can't use.\n if (isLoading) return <>{fallback ?? <DefaultSpinner />}</>;\n if (!session) {\n if (redirect) return <>{fallback ?? <DefaultSpinner />}</>;\n return <>{signedOut ?? null}</>;\n }\n return <>{children}</>;\n}\n"]}
1
+ {"version":3,"file":"require-session.js","sourceRoot":"","sources":["../../src/client/require-session.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAc,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AA8B9C,4EAA4E;AAC5E,MAAM,UAAU,qBAAqB;IACnC,MAAM,IAAI,GAAG,eAAe,CAAC,wBAAwB,CAAC,CAAC;IACvD,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,GAAG,GACP,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC3E,OAAO,GAAG,IAAI,WAAW,kBAAkB,CAAC,GAAG,CAAC,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc;IAC5B,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IAChD,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,KAAK,eAAe,CAAC,wBAAwB,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAC7B,QAAQ,EACR,QAAQ,EACR,QAAQ,GAAG,IAAI,EACf,SAAS,EACT,MAAM,GAAG,KAAK,GACM;IACpB,IAAI,MAAM;QAAE,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACnC,OAAO,CACL,KAAC,mBAAmB,IAClB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,SAAS,YAEnB,QAAQ,GACW,CACvB,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,EAC3B,QAAQ,EACR,QAAQ,EACR,QAAQ,GAAG,IAAI,EACf,SAAS,GAC2B;IACpC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,UAAU,EAAE,CAAC;IAC5C,0EAA0E;IAC1E,wEAAwE;IACxE,gCAAgC;IAChC,MAAM,aAAa,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEpC,MAAM,YAAY,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,IAAI,QAAQ,IAAI,CAAC,cAAc,EAAE,CAAC;IAE7E,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,IAAI,aAAa,CAAC,OAAO;YAAE,OAAO;QAClC,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO;QAC1C,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;QAC7B,yEAAyE;QACzE,wEAAwE;QACxE,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACnD,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAEnB,4EAA4E;IAC5E,yDAAyD;IACzD,IAAI,SAAS;QAAE,OAAO,4BAAG,QAAQ,IAAI,KAAC,cAAc,KAAG,GAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,QAAQ;YAAE,OAAO,4BAAG,QAAQ,IAAI,KAAC,cAAc,KAAG,GAAI,CAAC;QAC3D,OAAO,4BAAG,SAAS,IAAI,IAAI,GAAI,CAAC;IAClC,CAAC;IACD,OAAO,4BAAG,QAAQ,GAAI,CAAC;AACzB,CAAC","sourcesContent":["/**\n * Client-side session gate for an authenticated app shell.\n *\n * Wrap a template's private app shell with <RequireSession> so a logged-out\n * visitor is sent to the framework sign-in page instead of being left staring\n * at an infinite loading spinner.\n *\n * Why this exists in addition to the server-side auth guard (`runAuthGuard`,\n * which serves the onboarding/sign-in HTML for unauthenticated requests):\n * the server guard only protects requests that actually reach the Nitro\n * function. A statically-served / CDN-cached SPA shell, or a client-side\n * (React Router) navigation made after the session expired, never re-hits the\n * guard — so the app boots with no session, every data query 401s, and the UI\n * sticks on its loading state forever. This component closes that gap by\n * resolving the session on the client and redirecting when there is none.\n *\n * Place it INSIDE your providers (so the fallback is themed) but AROUND the\n * routed app layout:\n *\n * <AppProviders ...>\n * <RequireSession>\n * <AppLayout><Outlet /></AppLayout>\n * </RequireSession>\n * </AppProviders>\n *\n * Templates with public/anonymous routes (share pages, embeds) must NOT wrap\n * their whole app — gate only the private subtree, or pass `bypass` for the\n * surfaces that authenticate by another mechanism.\n */\nimport React, { useEffect, useRef } from \"react\";\n\nimport { agentNativePath } from \"./api-path.js\";\nimport { DefaultSpinner } from \"./DefaultSpinner.js\";\nimport { useSession } from \"./use-session.js\";\n\nexport interface RequireSessionProps {\n children: React.ReactNode;\n /**\n * Rendered while the session is being resolved and while a redirect is in\n * flight. Defaults to the framework `<DefaultSpinner />`.\n */\n fallback?: React.ReactNode;\n /**\n * When true (default), unauthenticated visitors are redirected to the\n * framework sign-in entry point (`/_agent-native/sign-in`) with a `return`\n * query pointing back at the current URL — so they land back here once\n * signed in. When false, `signedOut` is rendered instead and no navigation\n * happens.\n */\n redirect?: boolean;\n /**\n * Rendered for unauthenticated visitors when `redirect` is false. Ignored\n * when `redirect` is true.\n */\n signedOut?: React.ReactNode;\n /**\n * Skip the gate entirely and always render children. Use for surfaces that\n * authenticate by another mechanism (e.g. an embed/popout iframe carrying\n * its own token) so they are never bounced to the sign-in page.\n */\n bypass?: boolean;\n}\n\n/** Build the framework sign-in URL that returns to the current location. */\nexport function buildSignInReturnHref(): string {\n const base = agentNativePath(\"/_agent-native/sign-in\");\n if (typeof window === \"undefined\") return base;\n const ret =\n window.location.pathname + window.location.search + window.location.hash;\n return `${base}?return=${encodeURIComponent(ret)}`;\n}\n\n/**\n * True when the browser is already sitting on the framework sign-in entry\n * point. Redirecting to sign-in from here would append the current sign-in\n * URL as a fresh `?return=`, re-encode it, and navigate again — an infinite\n * loop of ever-growing `…sign-in?return=%252F…sign-in%253Freturn%253D…` URLs.\n *\n * This happens when the authenticated app shell (wrapped in `RequireSession`)\n * is served at the sign-in path instead of the framework login HTML — e.g.\n * under a base-path deploy where the SPA shell answers `/<app>/_agent-native/\n * sign-in`. The sign-in page is the framework's job, not the gate's: never\n * redirect to ourselves. Compared against `agentNativePath(...)` so it matches\n * whether or not the app is mounted under a base path.\n */\nexport function isOnSignInPage(): boolean {\n if (typeof window === \"undefined\") return false;\n return window.location.pathname === agentNativePath(\"/_agent-native/sign-in\");\n}\n\nexport function RequireSession({\n children,\n fallback,\n redirect = true,\n signedOut,\n bypass = false,\n}: RequireSessionProps) {\n if (bypass) return <>{children}</>;\n return (\n <ResolvedSessionGate\n fallback={fallback}\n redirect={redirect}\n signedOut={signedOut}\n >\n {children}\n </ResolvedSessionGate>\n );\n}\n\nfunction ResolvedSessionGate({\n children,\n fallback,\n redirect = true,\n signedOut,\n}: Omit<RequireSessionProps, \"bypass\">) {\n const { session, isLoading } = useSession();\n // Guard against firing the redirect more than once (effect re-runs, React\n // StrictMode double-invoke) — a second navigation while the first is in\n // flight is harmless but noisy.\n const redirectedRef = useRef(false);\n\n const mustRedirect = !isLoading && !session && redirect && !isOnSignInPage();\n\n useEffect(() => {\n if (!mustRedirect) return;\n if (redirectedRef.current) return;\n if (typeof window === \"undefined\") return;\n redirectedRef.current = true;\n // `replace` (not `assign`) so the dead authenticated URL doesn't land in\n // history — pressing Back after signing in shouldn't bounce here again.\n window.location.replace(buildSignInReturnHref());\n }, [mustRedirect]);\n\n // Still resolving, or redirect already in flight: show the loading fallback\n // rather than flashing app chrome the visitor can't use.\n if (isLoading) return <>{fallback ?? <DefaultSpinner />}</>;\n if (!session) {\n if (redirect) return <>{fallback ?? <DefaultSpinner />}</>;\n return <>{signedOut ?? null}</>;\n }\n return <>{children}</>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-action.d.ts","sourceRoot":"","sources":["../../src/client/use-action.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"use-action.d.ts","sourceRoot":"","sources":["../../src/client/use-action.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@
22
22
  * compatibility.
23
23
  */
24
24
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
25
+ import { trackEvent } from "./analytics.js";
25
26
  import { agentNativePath } from "./api-path.js";
26
27
  import { getBrowserTabId } from "./browser-tab-id.js";
27
28
  import { ensureEmbedAuthFetchInterceptor } from "./embed-auth.js";
@@ -156,7 +157,7 @@ function utf8ByteLength(value) {
156
157
  }
157
158
  return bytes;
158
159
  }
159
- async function actionFetch(name, method, params, options) {
160
+ async function performActionFetch(name, method, params, options) {
160
161
  ensureEmbedAuthFetchInterceptor();
161
162
  let url = `${ACTION_PREFIX}/${name}`;
162
163
  const headers = {
@@ -231,6 +232,7 @@ async function actionFetch(name, method, params, options) {
231
232
  try {
232
233
  try {
233
234
  res = await fetch(url, init);
235
+ options?.onResponse?.(res);
234
236
  }
235
237
  catch (err) {
236
238
  if (timedOut)
@@ -314,6 +316,120 @@ async function actionFetch(name, method, params, options) {
314
316
  }
315
317
  return (data ?? null);
316
318
  }
319
+ function actionTelemetryNow() {
320
+ return typeof performance !== "undefined" ? performance.now() : Date.now();
321
+ }
322
+ function parseServerTiming(response) {
323
+ const timings = new Map();
324
+ const value = response?.headers.get("server-timing");
325
+ if (!value)
326
+ return timings;
327
+ for (const entry of value.split(",")) {
328
+ const [rawName, ...params] = entry.trim().split(";");
329
+ const name = rawName?.trim();
330
+ if (!name)
331
+ continue;
332
+ const duration = params
333
+ .map((param) => /^dur=(.+)$/i.exec(param.trim())?.[1])
334
+ .find(Boolean);
335
+ const parsed = duration === undefined ? NaN : Number(duration);
336
+ if (Number.isFinite(parsed))
337
+ timings.set(name, parsed);
338
+ }
339
+ return timings;
340
+ }
341
+ function shouldTrackActionResponse(error, durationMs, response) {
342
+ if (error || durationMs >= 1_000)
343
+ return true;
344
+ if (response && response.status >= 400 && response.status < 500)
345
+ return true;
346
+ if (/\bstartup(?:-db)?\s*;/i.test(response?.headers.get("server-timing") ?? "")) {
347
+ return true;
348
+ }
349
+ const raw = import.meta.env
350
+ ?.VITE_AGENT_NATIVE_ACTION_TELEMETRY_SAMPLE_RATE;
351
+ const parsed = raw === undefined ? 0.1 : Number(raw);
352
+ const rate = Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : 0.1;
353
+ return Math.random() < rate;
354
+ }
355
+ async function actionFetch(name, method, params, options) {
356
+ const startedAt = actionTelemetryNow();
357
+ let response;
358
+ let responseAt;
359
+ let error;
360
+ try {
361
+ return await performActionFetch(name, method, params, {
362
+ ...options,
363
+ onResponse: (nextResponse) => {
364
+ response = nextResponse;
365
+ responseAt = actionTelemetryNow();
366
+ },
367
+ });
368
+ }
369
+ catch (caught) {
370
+ error = caught;
371
+ throw caught;
372
+ }
373
+ finally {
374
+ try {
375
+ const completedAt = actionTelemetryNow();
376
+ const durationMs = Math.max(0, completedAt - startedAt);
377
+ if (shouldTrackActionResponse(error, durationMs, response)) {
378
+ const ttfbMs = responseAt === undefined
379
+ ? undefined
380
+ : Math.max(0, responseAt - startedAt);
381
+ const serverTiming = parseServerTiming(response);
382
+ const serverDurationMs = serverTiming.get("app");
383
+ const errorStatus = Number(error?.status);
384
+ const statusCode = response?.status ??
385
+ (Number.isFinite(errorStatus) ? errorStatus : undefined);
386
+ const timedOut = error?.timedOut === true;
387
+ const cancelled = options?.signal?.aborted === true && !timedOut;
388
+ const contentLength = Number(response?.headers.get("content-length"));
389
+ trackEvent("action.response", {
390
+ request_id: response?.headers.get("x-agent-native-request-id") ?? undefined,
391
+ action: name,
392
+ method,
393
+ status_code: statusCode,
394
+ status_class: statusCode === undefined
395
+ ? "network"
396
+ : `${Math.floor(statusCode / 100)}xx`,
397
+ success: !error,
398
+ outcome: !error
399
+ ? "success"
400
+ : timedOut
401
+ ? "timeout"
402
+ : cancelled
403
+ ? "cancelled"
404
+ : response
405
+ ? "http-error"
406
+ : "network-error",
407
+ duration_ms: Math.round(durationMs),
408
+ ttfb_ms: ttfbMs === undefined ? undefined : Math.round(ttfbMs),
409
+ body_ms: ttfbMs === undefined ? undefined : Math.round(durationMs - ttfbMs),
410
+ server_duration_ms: serverDurationMs === undefined
411
+ ? undefined
412
+ : Math.round(serverDurationMs),
413
+ network_overhead_ms: ttfbMs === undefined || serverDurationMs === undefined
414
+ ? undefined
415
+ : Math.max(0, Math.round(ttfbMs - serverDurationMs)),
416
+ framework_ready_wait_ms: serverTiming.get("startup"),
417
+ db_operation_wall_ms: serverTiming.get("db"),
418
+ db_connect_total_ms: serverTiming.get("db-connect"),
419
+ db_slowest_operation_ms: serverTiming.get("db-slowest"),
420
+ startup_db_operation_wall_ms: serverTiming.get("startup-db"),
421
+ startup_db_connect_total_ms: serverTiming.get("startup-db-connect"),
422
+ response_bytes: Number.isFinite(contentLength) && contentLength >= 0
423
+ ? contentLength
424
+ : undefined,
425
+ });
426
+ }
427
+ }
428
+ catch {
429
+ // Performance telemetry must never change the action result.
430
+ }
431
+ }
432
+ }
317
433
  /**
318
434
  * Imperatively call an action from browser/client code.
319
435
  *
@@ -1 +1 @@
1
- {"version":3,"file":"use-action.js","sourceRoot":"","sources":["../../src/client/use-action.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client helpers for calling actions through the framework transport.\n *\n * Components should prefer `useActionQuery` / `useActionMutation`; use\n * `callAction` for imperative cases such as debounced search, prefetching, or\n * event handlers that do not fit a hook.\n *\n * ## End-to-end type safety\n *\n * When the action type registry is generated (via the Vite plugin or CLI),\n * `useActionQuery` and `useActionMutation` automatically infer the correct\n * return type and parameter types from the action definitions — no manual\n * type annotations needed.\n *\n * ```ts\n * // Fully typed — return type and params inferred from the action's defineAction()\n * const { data } = useActionQuery(\"list-forms\", { status: \"published\" });\n * // ^? Form[] (inferred from the action's run() return type)\n * ```\n *\n * Without the registry, the hooks fall back to `any` types for backward\n * compatibility.\n */\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\n\nimport { agentNativePath } from \"./api-path.js\";\nimport { getBrowserTabId } from \"./browser-tab-id.js\";\nimport { ensureEmbedAuthFetchInterceptor } from \"./embed-auth.js\";\n\nconst ACTION_PREFIX = agentNativePath(\"/_agent-native/actions\");\n\n/**\n * Upper bound on how long a single action fetch may stay in flight (headers\n * AND body). Converts a hung server/proxy/connection into a visible, typed\n * failure instead of a UI that spins forever. Generous on purpose: it sits\n * above every server-side budget (serverless function limits, hosted run\n * wall-clock), so it only fires when something is genuinely stuck.\n */\nconst DEFAULT_ACTION_TIMEOUT_MS = 60_000;\n\nfunction isAuthFailure(error: unknown): boolean {\n return (\n !!error &&\n typeof error === \"object\" &&\n \"status\" in error &&\n ((error as { status?: unknown }).status === 401 ||\n (error as { status?: unknown }).status === 403)\n );\n}\n\nfunction isActionTimeout(error: unknown): boolean {\n return (\n !!error &&\n typeof error === \"object\" &&\n (error as { timedOut?: unknown }).timedOut === true\n );\n}\n\n/** @internal exported for tests */\nexport function defaultActionQueryRetry(\n failureCount: number,\n error: unknown,\n): boolean {\n if (isAuthFailure(error)) return false;\n // A timeout already made the user wait the full timeout window once;\n // silently retrying would multiply that wait. Surface it instead.\n if (isActionTimeout(error)) return false;\n if (isBrowserResourceExhaustion(error)) return false;\n // Network-level failures never carry an HTTP `status` (actionFetch only\n // sets it after a response arrives). Chrome reports connection-pool\n // exhaustion (net::ERR_INSUFFICIENT_RESOURCES) as a generic \"Failed to\n // fetch\", indistinguishable from a transient blip — allow one retry, not\n // three, so an exhausted tab cannot sustain its own fetch storm.\n if (isNetworkLevelFailure(error)) return failureCount < 1;\n return failureCount < 3;\n}\n\n/** @internal alias kept for existing specs. */\nexport const shouldRetryActionQueryForError = defaultActionQueryRetry;\n\nfunction isBrowserResourceExhaustion(error: unknown): boolean {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"\";\n return /ERR_INSUFFICIENT_RESOURCES|insufficient resources/i.test(message);\n}\n\nfunction isNetworkLevelFailure(error: unknown): boolean {\n // Match the exact shape actionFetch produces for fetch-level failures (its\n // catch wraps the cause as \"Action <name> failed: <cause>\" and never sets a\n // status). Other status-less errors (test doubles, transport internals)\n // keep the standard three-retry policy.\n return (\n error instanceof Error &&\n (error as { status?: unknown }).status === undefined &&\n /^Action .+ failed: /.test(error.message)\n );\n}\n\n/**\n * Default retry backoff for action queries. React Query's stock retryDelay\n * (1s → 2s → 4s) makes a failing query sit on a spinner for ~7s before the\n * error surfaces; interactive data fetches want failures visible fast.\n *\n * @internal exported for tests\n */\nexport function defaultActionQueryRetryDelay(failureCount: number): number {\n return Math.min(500 * 2 ** failureCount, 2_000);\n}\n\n// ---------------------------------------------------------------------------\n// Action type registry — augmented by generated code\n// ---------------------------------------------------------------------------\n\n/**\n * Action type registry. This interface is empty by default and gets augmented\n * by the auto-generated `.generated/action-types.d.ts` file. When augmented,\n * it maps action names to their parameter and return types, enabling\n * end-to-end type safety for `useActionQuery` and `useActionMutation`.\n */\ndeclare global {\n interface AgentNativeActionRegistry {}\n}\n\nexport interface ActionRegistry extends AgentNativeActionRegistry {}\n\n/** Resolves to the union of registered action names, or `string` if no registry exists. */\ntype ActionName = keyof ActionRegistry extends never\n ? string\n : (keyof ActionRegistry & string) | (string & {});\n\n/** Resolves the return type of an action, or `any` if not in the registry. */\ntype ActionResult<T extends string> = T extends keyof ActionRegistry\n ? ActionRegistry[T] extends { result: infer R }\n ? R\n : any\n : any;\n\n/** Resolves the parameter type of an action, or `Record<string, any>` if not in the registry. */\ntype ActionParams<T extends string> = T extends keyof ActionRegistry\n ? ActionRegistry[T] extends { params: infer P }\n ? P\n : Record<string, any>\n : Record<string, any>;\n\nexport type ClientActionMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\nexport interface ClientActionCallOptions {\n method?: ClientActionMethod;\n /** Abort signal for the underlying fetch. */\n signal?: AbortSignal;\n /** Override the default 60s fetch timeout for long-running actions. */\n timeoutMs?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Fetch helper\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the browser's IANA timezone (e.g. \"America/Los_Angeles\"). This is\n * sent on every action request as `x-user-timezone` so server-side defaults\n * like \"today\" honor the user's local day rather than the server's UTC clock.\n */\nfunction resolveUserTimezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function serializeActionQueryParams(\n params: Record<string, any>,\n): string {\n const qs = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n appendActionQueryParam(qs, key, value);\n }\n return qs.toString();\n}\n\nfunction appendActionQueryParam(\n qs: URLSearchParams,\n key: string,\n value: unknown,\n) {\n if (value === null || value === undefined) return;\n if (Array.isArray(value)) {\n // Use bracket keys so a one-item array still arrives as an array after the\n // server parses URLSearchParams. Repeated bare keys lose that distinction.\n for (const item of value) {\n appendActionQueryParam(qs, `${key}[]`, item);\n }\n return;\n }\n qs.append(key, String(value));\n}\n\nexport interface ActionFetchOptions {\n /**\n * Abort signal from the caller (React Query passes one per queryFn\n * invocation so superseded requests — key change, unmount, refetch — cancel\n * the underlying network request instead of hogging a connection slot).\n */\n signal?: AbortSignal;\n /** Per-call override for the fetch timeout. */\n timeoutMs?: number;\n /** Keep the request alive while the document is being unloaded. */\n keepalive?: boolean;\n /** Pre-serialized mutation body used by the keepalive budget coordinator. */\n serializedBody?: string;\n /** Omit the tab echo-suppression tag for imperative callers. */\n includeRequestSource?: boolean;\n}\n\n/**\n * Conservative per-document keepalive body budget. Browsers commonly enforce\n * an approximately 64 KiB aggregate limit across every in-flight keepalive\n * request; leaving headroom for other framework traffic prevents a request\n * that passed our guard from being rejected by the browser at send time.\n */\nexport const ACTION_KEEPALIVE_BODY_BUDGET_BYTES = 48_000;\n\nlet reservedKeepaliveBodyBytes = 0;\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).byteLength;\n }\n\n let bytes = 0;\n for (const character of value) {\n const codePoint = character.codePointAt(0) ?? 0;\n bytes +=\n codePoint <= 0x7f\n ? 1\n : codePoint <= 0x7ff\n ? 2\n : codePoint <= 0xffff\n ? 3\n : 4;\n }\n return bytes;\n}\n\nasync function actionFetch<T>(\n name: string,\n method: string,\n params?: Record<string, any>,\n options?: ActionFetchOptions,\n): Promise<T> {\n ensureEmbedAuthFetchInterceptor();\n let url = `${ACTION_PREFIX}/${name}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n // Tag browser-originated action calls so the server can set\n // `ctx.caller = \"frontend\"` (vs a bare programmatic `\"http\"` POST).\n // Mirrors the X-Agent-Native-Tool-Bridge: 1 convention. The header is\n // safe to expose: CORS allows it (see action-routes.ts) and it carries\n // no auth weight — it only narrows the caller tag.\n \"X-Agent-Native-Frontend\": \"1\",\n ...(options?.includeRequestSource !== false\n ? {\n // The server copies this onto the emitted action sync event.\n // useDbSync can then ignore the echo in this tab while other tabs\n // still refresh.\n \"X-Request-Source\": getBrowserTabId(),\n }\n : {}),\n };\n const tz = resolveUserTimezone();\n if (tz) headers[\"x-user-timezone\"] = tz;\n const init: RequestInit = {\n method,\n headers,\n cache: \"no-store\",\n keepalive: options?.keepalive,\n };\n\n if (method === \"GET\" && params && Object.keys(params).length > 0) {\n // Skip null/undefined so optional filters don't turn into literal \"null\"\n // strings in the query string (e.g. `?folderId=null`).\n const qs = serializeActionQueryParams(params);\n if (qs) url += `?${qs}`;\n } else if (method !== \"GET\" && params) {\n init.body = options?.serializedBody ?? JSON.stringify(params);\n }\n\n // One controller drives both cancellation sources: the caller's signal\n // (superseded query, unmount) and the timeout. The timer stays armed until\n // the BODY is fully read — headers arriving quickly while the body stalls\n // is exactly the hang this bounds.\n const outerSignal = options?.signal;\n const timeoutMs = options?.timeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;\n const controller =\n typeof AbortController === \"undefined\" ? null : new AbortController();\n const onOuterAbort = () => controller?.abort();\n if (outerSignal && controller) {\n if (outerSignal.aborted) controller.abort();\n else outerSignal.addEventListener(\"abort\", onOuterAbort, { once: true });\n }\n let timedOut = false;\n const timer = controller\n ? setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs)\n : null;\n if (controller) init.signal = controller.signal;\n\n const throwTimeout = (): never => {\n const error = new Error(\n `Action ${name} timed out after ${Math.round(timeoutMs / 1000)}s`,\n );\n (error as any).timedOut = true;\n (error as any).status = 408;\n throw error;\n };\n\n let res: Response;\n let raw = \"\";\n let readFailed = false;\n let readError: unknown;\n try {\n try {\n res = await fetch(url, init);\n } catch (err) {\n if (timedOut) throwTimeout();\n // Caller-initiated cancellation — rethrow untouched so React Query\n // recognizes it as a cancellation rather than a query failure.\n if (outerSignal?.aborted) throw err;\n // Network failures, CORS, server unreachable, etc. — give the caller a\n // useful message instead of the opaque \"Failed to fetch\".\n const cause = err instanceof Error ? err.message : String(err);\n throw new Error(`Action ${name} failed: ${cause}`);\n }\n\n // 204 No Content — nothing to parse.\n if (res.status === 204) return null as T;\n\n // Read the body as text first so we can:\n // - tolerate empty bodies (avoids \"Unexpected end of JSON input\")\n // - surface non-JSON error responses (HTML 401/404 pages, plain text, etc.)\n // - preserve the original HTTP status in the thrown error\n // Track read failures separately from \"no body\" — a stream interruption /\n // decode failure on a 2xx response should error rather than silently\n // succeed with `null`.\n try {\n raw = await res.text();\n } catch (err) {\n if (timedOut) throwTimeout();\n if (outerSignal?.aborted) throw err;\n readFailed = true;\n readError = err;\n }\n } finally {\n if (timer) clearTimeout(timer);\n if (outerSignal) outerSignal.removeEventListener(\"abort\", onOuterAbort);\n }\n\n let data: any = undefined;\n let parseFailed = false;\n if (raw.length > 0) {\n try {\n data = JSON.parse(raw);\n } catch {\n // Body wasn't JSON — keep `data` undefined and use the raw text below.\n parseFailed = true;\n }\n }\n\n if (!res.ok) {\n const message =\n (data && (data.error || data.message)) ||\n // Truncate non-JSON bodies so we don't dump entire HTML pages into the\n // console, but still give the developer a hint as to what came back.\n (raw && raw.slice(0, 200)) ||\n res.statusText ||\n `HTTP ${res.status}`;\n const error = new Error(`Action ${name} failed: ${message}`);\n (error as any).status = res.status;\n throw error;\n }\n\n // 2xx but the body couldn't even be read (mid-stream abort, decode failure,\n // etc.). Don't silently treat that as a `null` success.\n if (readFailed) {\n const cause =\n readError instanceof Error ? readError.message : String(readError);\n const error = new Error(\n `Action ${name} returned ${res.status} but the body could not be read: ${cause}`,\n );\n (error as any).status = res.status;\n throw error;\n }\n\n // 2xx with a non-empty, non-JSON body. Action callers expect typed data, so\n // returning `null` here would silently mask a real server bug (e.g. a proxy\n // returning HTML 200 instead of JSON). Throw instead — empty bodies (handled\n // above by the `raw.length > 0` guard and the 204 short-circuit) still\n // correctly resolve to `null`.\n if (parseFailed) {\n const error = new Error(\n `Action ${name} returned a non-JSON ${res.status} response: ${raw.slice(0, 200)}`,\n );\n (error as any).status = res.status;\n throw error;\n }\n\n return (data ?? (null as unknown)) as T;\n}\n\n/**\n * Imperatively call an action from browser/client code.\n *\n * Prefer `useActionQuery` / `useActionMutation` in React render flows. Use this\n * helper when a hook is not ergonomic; do not hand-write fetch calls to\n * `/_agent-native/actions/*` in components.\n */\nexport function callAction<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options: ClientActionCallOptions = {},\n): Promise<TResult extends undefined ? ActionResult<TName> : TResult> {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n return actionFetch<R>(actionName, options.method ?? \"POST\", params, {\n signal: options.signal,\n timeoutMs: options.timeoutMs,\n includeRequestSource: false,\n });\n}\n\nexport type KeepaliveActionCallRejectionReason =\n | \"body-too-large\"\n | \"budget-exhausted\";\n\nexport type KeepaliveActionCallResult<TResult> =\n | {\n accepted: true;\n bodyBytes: number;\n completion: Promise<TResult>;\n }\n | {\n accepted: false;\n bodyBytes: number;\n reason: KeepaliveActionCallRejectionReason;\n completion: null;\n };\n\n/**\n * Attempts an unload-safe action call without exceeding the browser's shared\n * keepalive request budget. The reservation remains held until the response\n * body has completed, because browsers count every in-flight keepalive body\n * against the same per-document quota.\n *\n * A rejected attempt is deliberately synchronous so callers can fall back to\n * a durable outbox before returning from `pagehide`.\n */\nexport function tryCallActionKeepalive<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options: Omit<ClientActionCallOptions, \"method\"> = {},\n): KeepaliveActionCallResult<\n TResult extends undefined ? ActionResult<TName> : TResult\n> {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n const serializedBody = JSON.stringify(params ?? {});\n const bodyBytes = utf8ByteLength(serializedBody);\n\n if (bodyBytes > ACTION_KEEPALIVE_BODY_BUDGET_BYTES) {\n return {\n accepted: false,\n bodyBytes,\n reason: \"body-too-large\",\n completion: null,\n };\n }\n\n if (\n reservedKeepaliveBodyBytes + bodyBytes >\n ACTION_KEEPALIVE_BODY_BUDGET_BYTES\n ) {\n return {\n accepted: false,\n bodyBytes,\n reason: \"budget-exhausted\",\n completion: null,\n };\n }\n\n reservedKeepaliveBodyBytes += bodyBytes;\n const completion = actionFetch<R>(actionName, \"POST\", params, {\n signal: options.signal,\n timeoutMs: options.timeoutMs,\n keepalive: true,\n serializedBody,\n }).finally(() => {\n reservedKeepaliveBodyBytes = Math.max(\n 0,\n reservedKeepaliveBodyBytes - bodyBytes,\n );\n });\n\n return { accepted: true, bodyBytes, completion };\n}\n\n// ---------------------------------------------------------------------------\n// Query hook\n// ---------------------------------------------------------------------------\n\n/**\n * Query an action exposed as GET.\n *\n * When the action type registry is generated, the return type and parameter\n * types are inferred automatically from the action's `defineAction()` call.\n *\n * ```ts\n * // Type-safe — no manual generic needed\n * const { data } = useActionQuery(\"list-meals\", { date: \"2025-01-01\" });\n *\n * // Manual override still works when needed\n * const { data } = useActionQuery<CustomType>(\"list-meals\");\n * ```\n */\nexport function useActionQuery<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options?: Omit<\n UseQueryOptions<TResult extends undefined ? ActionResult<TName> : TResult>,\n \"queryKey\" | \"queryFn\"\n >,\n) {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n return useQuery<R>({\n queryKey: [\"action\", actionName, params],\n // Thread React Query's per-fetch AbortSignal into the network request so\n // superseded fetches (key change, unmount, rapid refetch) actually cancel\n // instead of holding a per-origin connection slot until they finish.\n queryFn: ({ signal }) =>\n actionFetch<R>(actionName, \"GET\", params, { signal }),\n retry: defaultActionQueryRetry,\n retryDelay: defaultActionQueryRetryDelay,\n ...options,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Mutation hook\n// ---------------------------------------------------------------------------\n\n/**\n * Mutate via an action exposed as POST (default), PUT, or DELETE.\n *\n * When the action type registry is generated, the return type and parameter\n * types are inferred automatically.\n *\n * ```ts\n * // Type-safe\n * const { mutate } = useActionMutation(\"log-meal\");\n * mutate({ name: \"Salad\", calories: 350 });\n * ```\n */\nexport function useActionMutation<\n TData = undefined,\n TVariables = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n options?: Omit<\n UseMutationOptions<\n TData extends undefined ? ActionResult<TName> : TData,\n Error,\n TVariables extends undefined ? ActionParams<TName> : TVariables\n >,\n \"mutationFn\"\n > & {\n method?: \"POST\" | \"PUT\" | \"DELETE\";\n skipActionQueryInvalidation?: boolean;\n },\n) {\n const queryClient = useQueryClient();\n const {\n method: methodOpt,\n onSuccess,\n skipActionQueryInvalidation = false,\n ...restOptions\n } = options ?? ({} as any);\n const method = methodOpt ?? \"POST\";\n\n type D = TData extends undefined ? ActionResult<TName> : TData;\n type V = TVariables extends undefined ? ActionParams<TName> : TVariables;\n\n return useMutation<D, Error, V>({\n ...restOptions,\n mutationFn: (params) =>\n actionFetch<D>(actionName, method, params as Record<string, any>),\n onSuccess: (...args: [any, any, any]) => {\n // Most mutations change app data broadly. High-volume background\n // mutations can opt out and perform narrower invalidation in onSuccess.\n if (!skipActionQueryInvalidation) {\n queryClient.invalidateQueries({ queryKey: [\"action\"] });\n }\n (onSuccess as Function)?.(...args);\n },\n });\n}\n"]}
1
+ {"version":3,"file":"use-action.js","sourceRoot":"","sources":["../../src/client/use-action.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Client helpers for calling actions through the framework transport.\n *\n * Components should prefer `useActionQuery` / `useActionMutation`; use\n * `callAction` for imperative cases such as debounced search, prefetching, or\n * event handlers that do not fit a hook.\n *\n * ## End-to-end type safety\n *\n * When the action type registry is generated (via the Vite plugin or CLI),\n * `useActionQuery` and `useActionMutation` automatically infer the correct\n * return type and parameter types from the action definitions — no manual\n * type annotations needed.\n *\n * ```ts\n * // Fully typed — return type and params inferred from the action's defineAction()\n * const { data } = useActionQuery(\"list-forms\", { status: \"published\" });\n * // ^? Form[] (inferred from the action's run() return type)\n * ```\n *\n * Without the registry, the hooks fall back to `any` types for backward\n * compatibility.\n */\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport type {\n UseQueryOptions,\n UseMutationOptions,\n} from \"@tanstack/react-query\";\n\nimport { trackEvent } from \"./analytics.js\";\nimport { agentNativePath } from \"./api-path.js\";\nimport { getBrowserTabId } from \"./browser-tab-id.js\";\nimport { ensureEmbedAuthFetchInterceptor } from \"./embed-auth.js\";\n\nconst ACTION_PREFIX = agentNativePath(\"/_agent-native/actions\");\n\n/**\n * Upper bound on how long a single action fetch may stay in flight (headers\n * AND body). Converts a hung server/proxy/connection into a visible, typed\n * failure instead of a UI that spins forever. Generous on purpose: it sits\n * above every server-side budget (serverless function limits, hosted run\n * wall-clock), so it only fires when something is genuinely stuck.\n */\nconst DEFAULT_ACTION_TIMEOUT_MS = 60_000;\n\nfunction isAuthFailure(error: unknown): boolean {\n return (\n !!error &&\n typeof error === \"object\" &&\n \"status\" in error &&\n ((error as { status?: unknown }).status === 401 ||\n (error as { status?: unknown }).status === 403)\n );\n}\n\nfunction isActionTimeout(error: unknown): boolean {\n return (\n !!error &&\n typeof error === \"object\" &&\n (error as { timedOut?: unknown }).timedOut === true\n );\n}\n\n/** @internal exported for tests */\nexport function defaultActionQueryRetry(\n failureCount: number,\n error: unknown,\n): boolean {\n if (isAuthFailure(error)) return false;\n // A timeout already made the user wait the full timeout window once;\n // silently retrying would multiply that wait. Surface it instead.\n if (isActionTimeout(error)) return false;\n if (isBrowserResourceExhaustion(error)) return false;\n // Network-level failures never carry an HTTP `status` (actionFetch only\n // sets it after a response arrives). Chrome reports connection-pool\n // exhaustion (net::ERR_INSUFFICIENT_RESOURCES) as a generic \"Failed to\n // fetch\", indistinguishable from a transient blip — allow one retry, not\n // three, so an exhausted tab cannot sustain its own fetch storm.\n if (isNetworkLevelFailure(error)) return failureCount < 1;\n return failureCount < 3;\n}\n\n/** @internal alias kept for existing specs. */\nexport const shouldRetryActionQueryForError = defaultActionQueryRetry;\n\nfunction isBrowserResourceExhaustion(error: unknown): boolean {\n const message =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"\";\n return /ERR_INSUFFICIENT_RESOURCES|insufficient resources/i.test(message);\n}\n\nfunction isNetworkLevelFailure(error: unknown): boolean {\n // Match the exact shape actionFetch produces for fetch-level failures (its\n // catch wraps the cause as \"Action <name> failed: <cause>\" and never sets a\n // status). Other status-less errors (test doubles, transport internals)\n // keep the standard three-retry policy.\n return (\n error instanceof Error &&\n (error as { status?: unknown }).status === undefined &&\n /^Action .+ failed: /.test(error.message)\n );\n}\n\n/**\n * Default retry backoff for action queries. React Query's stock retryDelay\n * (1s → 2s → 4s) makes a failing query sit on a spinner for ~7s before the\n * error surfaces; interactive data fetches want failures visible fast.\n *\n * @internal exported for tests\n */\nexport function defaultActionQueryRetryDelay(failureCount: number): number {\n return Math.min(500 * 2 ** failureCount, 2_000);\n}\n\n// ---------------------------------------------------------------------------\n// Action type registry — augmented by generated code\n// ---------------------------------------------------------------------------\n\n/**\n * Action type registry. This interface is empty by default and gets augmented\n * by the auto-generated `.generated/action-types.d.ts` file. When augmented,\n * it maps action names to their parameter and return types, enabling\n * end-to-end type safety for `useActionQuery` and `useActionMutation`.\n */\ndeclare global {\n interface AgentNativeActionRegistry {}\n}\n\nexport interface ActionRegistry extends AgentNativeActionRegistry {}\n\n/** Resolves to the union of registered action names, or `string` if no registry exists. */\ntype ActionName = keyof ActionRegistry extends never\n ? string\n : (keyof ActionRegistry & string) | (string & {});\n\n/** Resolves the return type of an action, or `any` if not in the registry. */\ntype ActionResult<T extends string> = T extends keyof ActionRegistry\n ? ActionRegistry[T] extends { result: infer R }\n ? R\n : any\n : any;\n\n/** Resolves the parameter type of an action, or `Record<string, any>` if not in the registry. */\ntype ActionParams<T extends string> = T extends keyof ActionRegistry\n ? ActionRegistry[T] extends { params: infer P }\n ? P\n : Record<string, any>\n : Record<string, any>;\n\nexport type ClientActionMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n\nexport interface ClientActionCallOptions {\n method?: ClientActionMethod;\n /** Abort signal for the underlying fetch. */\n signal?: AbortSignal;\n /** Override the default 60s fetch timeout for long-running actions. */\n timeoutMs?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Fetch helper\n// ---------------------------------------------------------------------------\n\n/**\n * Resolve the browser's IANA timezone (e.g. \"America/Los_Angeles\"). This is\n * sent on every action request as `x-user-timezone` so server-side defaults\n * like \"today\" honor the user's local day rather than the server's UTC clock.\n */\nfunction resolveUserTimezone(): string | undefined {\n try {\n return Intl.DateTimeFormat().resolvedOptions().timeZone || undefined;\n } catch {\n return undefined;\n }\n}\n\nexport function serializeActionQueryParams(\n params: Record<string, any>,\n): string {\n const qs = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n appendActionQueryParam(qs, key, value);\n }\n return qs.toString();\n}\n\nfunction appendActionQueryParam(\n qs: URLSearchParams,\n key: string,\n value: unknown,\n) {\n if (value === null || value === undefined) return;\n if (Array.isArray(value)) {\n // Use bracket keys so a one-item array still arrives as an array after the\n // server parses URLSearchParams. Repeated bare keys lose that distinction.\n for (const item of value) {\n appendActionQueryParam(qs, `${key}[]`, item);\n }\n return;\n }\n qs.append(key, String(value));\n}\n\nexport interface ActionFetchOptions {\n /**\n * Abort signal from the caller (React Query passes one per queryFn\n * invocation so superseded requests — key change, unmount, refetch — cancel\n * the underlying network request instead of hogging a connection slot).\n */\n signal?: AbortSignal;\n /** Per-call override for the fetch timeout. */\n timeoutMs?: number;\n /** Keep the request alive while the document is being unloaded. */\n keepalive?: boolean;\n /** Pre-serialized mutation body used by the keepalive budget coordinator. */\n serializedBody?: string;\n /** Omit the tab echo-suppression tag for imperative callers. */\n includeRequestSource?: boolean;\n}\n\ntype InternalActionFetchOptions = ActionFetchOptions & {\n onResponse?: (response: Response) => void;\n};\n\n/**\n * Conservative per-document keepalive body budget. Browsers commonly enforce\n * an approximately 64 KiB aggregate limit across every in-flight keepalive\n * request; leaving headroom for other framework traffic prevents a request\n * that passed our guard from being rejected by the browser at send time.\n */\nexport const ACTION_KEEPALIVE_BODY_BUDGET_BYTES = 48_000;\n\nlet reservedKeepaliveBodyBytes = 0;\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).byteLength;\n }\n\n let bytes = 0;\n for (const character of value) {\n const codePoint = character.codePointAt(0) ?? 0;\n bytes +=\n codePoint <= 0x7f\n ? 1\n : codePoint <= 0x7ff\n ? 2\n : codePoint <= 0xffff\n ? 3\n : 4;\n }\n return bytes;\n}\n\nasync function performActionFetch<T>(\n name: string,\n method: string,\n params?: Record<string, any>,\n options?: InternalActionFetchOptions,\n): Promise<T> {\n ensureEmbedAuthFetchInterceptor();\n let url = `${ACTION_PREFIX}/${name}`;\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n // Tag browser-originated action calls so the server can set\n // `ctx.caller = \"frontend\"` (vs a bare programmatic `\"http\"` POST).\n // Mirrors the X-Agent-Native-Tool-Bridge: 1 convention. The header is\n // safe to expose: CORS allows it (see action-routes.ts) and it carries\n // no auth weight — it only narrows the caller tag.\n \"X-Agent-Native-Frontend\": \"1\",\n ...(options?.includeRequestSource !== false\n ? {\n // The server copies this onto the emitted action sync event.\n // useDbSync can then ignore the echo in this tab while other tabs\n // still refresh.\n \"X-Request-Source\": getBrowserTabId(),\n }\n : {}),\n };\n const tz = resolveUserTimezone();\n if (tz) headers[\"x-user-timezone\"] = tz;\n const init: RequestInit = {\n method,\n headers,\n cache: \"no-store\",\n keepalive: options?.keepalive,\n };\n\n if (method === \"GET\" && params && Object.keys(params).length > 0) {\n // Skip null/undefined so optional filters don't turn into literal \"null\"\n // strings in the query string (e.g. `?folderId=null`).\n const qs = serializeActionQueryParams(params);\n if (qs) url += `?${qs}`;\n } else if (method !== \"GET\" && params) {\n init.body = options?.serializedBody ?? JSON.stringify(params);\n }\n\n // One controller drives both cancellation sources: the caller's signal\n // (superseded query, unmount) and the timeout. The timer stays armed until\n // the BODY is fully read — headers arriving quickly while the body stalls\n // is exactly the hang this bounds.\n const outerSignal = options?.signal;\n const timeoutMs = options?.timeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;\n const controller =\n typeof AbortController === \"undefined\" ? null : new AbortController();\n const onOuterAbort = () => controller?.abort();\n if (outerSignal && controller) {\n if (outerSignal.aborted) controller.abort();\n else outerSignal.addEventListener(\"abort\", onOuterAbort, { once: true });\n }\n let timedOut = false;\n const timer = controller\n ? setTimeout(() => {\n timedOut = true;\n controller.abort();\n }, timeoutMs)\n : null;\n if (controller) init.signal = controller.signal;\n\n const throwTimeout = (): never => {\n const error = new Error(\n `Action ${name} timed out after ${Math.round(timeoutMs / 1000)}s`,\n );\n (error as any).timedOut = true;\n (error as any).status = 408;\n throw error;\n };\n\n let res: Response;\n let raw = \"\";\n let readFailed = false;\n let readError: unknown;\n try {\n try {\n res = await fetch(url, init);\n options?.onResponse?.(res);\n } catch (err) {\n if (timedOut) throwTimeout();\n // Caller-initiated cancellation — rethrow untouched so React Query\n // recognizes it as a cancellation rather than a query failure.\n if (outerSignal?.aborted) throw err;\n // Network failures, CORS, server unreachable, etc. — give the caller a\n // useful message instead of the opaque \"Failed to fetch\".\n const cause = err instanceof Error ? err.message : String(err);\n throw new Error(`Action ${name} failed: ${cause}`);\n }\n\n // 204 No Content — nothing to parse.\n if (res.status === 204) return null as T;\n\n // Read the body as text first so we can:\n // - tolerate empty bodies (avoids \"Unexpected end of JSON input\")\n // - surface non-JSON error responses (HTML 401/404 pages, plain text, etc.)\n // - preserve the original HTTP status in the thrown error\n // Track read failures separately from \"no body\" — a stream interruption /\n // decode failure on a 2xx response should error rather than silently\n // succeed with `null`.\n try {\n raw = await res.text();\n } catch (err) {\n if (timedOut) throwTimeout();\n if (outerSignal?.aborted) throw err;\n readFailed = true;\n readError = err;\n }\n } finally {\n if (timer) clearTimeout(timer);\n if (outerSignal) outerSignal.removeEventListener(\"abort\", onOuterAbort);\n }\n\n let data: any = undefined;\n let parseFailed = false;\n if (raw.length > 0) {\n try {\n data = JSON.parse(raw);\n } catch {\n // Body wasn't JSON — keep `data` undefined and use the raw text below.\n parseFailed = true;\n }\n }\n\n if (!res.ok) {\n const message =\n (data && (data.error || data.message)) ||\n // Truncate non-JSON bodies so we don't dump entire HTML pages into the\n // console, but still give the developer a hint as to what came back.\n (raw && raw.slice(0, 200)) ||\n res.statusText ||\n `HTTP ${res.status}`;\n const error = new Error(`Action ${name} failed: ${message}`);\n (error as any).status = res.status;\n throw error;\n }\n\n // 2xx but the body couldn't even be read (mid-stream abort, decode failure,\n // etc.). Don't silently treat that as a `null` success.\n if (readFailed) {\n const cause =\n readError instanceof Error ? readError.message : String(readError);\n const error = new Error(\n `Action ${name} returned ${res.status} but the body could not be read: ${cause}`,\n );\n (error as any).status = res.status;\n throw error;\n }\n\n // 2xx with a non-empty, non-JSON body. Action callers expect typed data, so\n // returning `null` here would silently mask a real server bug (e.g. a proxy\n // returning HTML 200 instead of JSON). Throw instead — empty bodies (handled\n // above by the `raw.length > 0` guard and the 204 short-circuit) still\n // correctly resolve to `null`.\n if (parseFailed) {\n const error = new Error(\n `Action ${name} returned a non-JSON ${res.status} response: ${raw.slice(0, 200)}`,\n );\n (error as any).status = res.status;\n throw error;\n }\n\n return (data ?? (null as unknown)) as T;\n}\n\nfunction actionTelemetryNow(): number {\n return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nfunction parseServerTiming(\n response: Response | undefined,\n): Map<string, number> {\n const timings = new Map<string, number>();\n const value = response?.headers.get(\"server-timing\");\n if (!value) return timings;\n\n for (const entry of value.split(\",\")) {\n const [rawName, ...params] = entry.trim().split(\";\");\n const name = rawName?.trim();\n if (!name) continue;\n const duration = params\n .map((param) => /^dur=(.+)$/i.exec(param.trim())?.[1])\n .find(Boolean);\n const parsed = duration === undefined ? NaN : Number(duration);\n if (Number.isFinite(parsed)) timings.set(name, parsed);\n }\n return timings;\n}\n\nfunction shouldTrackActionResponse(\n error: unknown,\n durationMs: number,\n response: Response | undefined,\n): boolean {\n if (error || durationMs >= 1_000) return true;\n if (response && response.status >= 400 && response.status < 500) return true;\n if (\n /\\bstartup(?:-db)?\\s*;/i.test(response?.headers.get(\"server-timing\") ?? \"\")\n ) {\n return true;\n }\n const raw = (import.meta.env as Record<string, string | undefined>)\n ?.VITE_AGENT_NATIVE_ACTION_TELEMETRY_SAMPLE_RATE;\n const parsed = raw === undefined ? 0.1 : Number(raw);\n const rate = Number.isFinite(parsed) ? Math.max(0, Math.min(1, parsed)) : 0.1;\n return Math.random() < rate;\n}\n\nasync function actionFetch<T>(\n name: string,\n method: string,\n params?: Record<string, any>,\n options?: ActionFetchOptions,\n): Promise<T> {\n const startedAt = actionTelemetryNow();\n let response: Response | undefined;\n let responseAt: number | undefined;\n let error: unknown;\n\n try {\n return await performActionFetch<T>(name, method, params, {\n ...options,\n onResponse: (nextResponse) => {\n response = nextResponse;\n responseAt = actionTelemetryNow();\n },\n });\n } catch (caught) {\n error = caught;\n throw caught;\n } finally {\n try {\n const completedAt = actionTelemetryNow();\n const durationMs = Math.max(0, completedAt - startedAt);\n if (shouldTrackActionResponse(error, durationMs, response)) {\n const ttfbMs =\n responseAt === undefined\n ? undefined\n : Math.max(0, responseAt - startedAt);\n const serverTiming = parseServerTiming(response);\n const serverDurationMs = serverTiming.get(\"app\");\n const errorStatus = Number(\n (error as { status?: unknown } | undefined)?.status,\n );\n const statusCode =\n response?.status ??\n (Number.isFinite(errorStatus) ? errorStatus : undefined);\n const timedOut =\n (error as { timedOut?: unknown } | undefined)?.timedOut === true;\n const cancelled = options?.signal?.aborted === true && !timedOut;\n const contentLength = Number(response?.headers.get(\"content-length\"));\n\n trackEvent(\"action.response\", {\n request_id:\n response?.headers.get(\"x-agent-native-request-id\") ?? undefined,\n action: name,\n method,\n status_code: statusCode,\n status_class:\n statusCode === undefined\n ? \"network\"\n : `${Math.floor(statusCode / 100)}xx`,\n success: !error,\n outcome: !error\n ? \"success\"\n : timedOut\n ? \"timeout\"\n : cancelled\n ? \"cancelled\"\n : response\n ? \"http-error\"\n : \"network-error\",\n duration_ms: Math.round(durationMs),\n ttfb_ms: ttfbMs === undefined ? undefined : Math.round(ttfbMs),\n body_ms:\n ttfbMs === undefined ? undefined : Math.round(durationMs - ttfbMs),\n server_duration_ms:\n serverDurationMs === undefined\n ? undefined\n : Math.round(serverDurationMs),\n network_overhead_ms:\n ttfbMs === undefined || serverDurationMs === undefined\n ? undefined\n : Math.max(0, Math.round(ttfbMs - serverDurationMs)),\n framework_ready_wait_ms: serverTiming.get(\"startup\"),\n db_operation_wall_ms: serverTiming.get(\"db\"),\n db_connect_total_ms: serverTiming.get(\"db-connect\"),\n db_slowest_operation_ms: serverTiming.get(\"db-slowest\"),\n startup_db_operation_wall_ms: serverTiming.get(\"startup-db\"),\n startup_db_connect_total_ms: serverTiming.get(\"startup-db-connect\"),\n response_bytes:\n Number.isFinite(contentLength) && contentLength >= 0\n ? contentLength\n : undefined,\n });\n }\n } catch {\n // Performance telemetry must never change the action result.\n }\n }\n}\n\n/**\n * Imperatively call an action from browser/client code.\n *\n * Prefer `useActionQuery` / `useActionMutation` in React render flows. Use this\n * helper when a hook is not ergonomic; do not hand-write fetch calls to\n * `/_agent-native/actions/*` in components.\n */\nexport function callAction<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options: ClientActionCallOptions = {},\n): Promise<TResult extends undefined ? ActionResult<TName> : TResult> {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n return actionFetch<R>(actionName, options.method ?? \"POST\", params, {\n signal: options.signal,\n timeoutMs: options.timeoutMs,\n includeRequestSource: false,\n });\n}\n\nexport type KeepaliveActionCallRejectionReason =\n | \"body-too-large\"\n | \"budget-exhausted\";\n\nexport type KeepaliveActionCallResult<TResult> =\n | {\n accepted: true;\n bodyBytes: number;\n completion: Promise<TResult>;\n }\n | {\n accepted: false;\n bodyBytes: number;\n reason: KeepaliveActionCallRejectionReason;\n completion: null;\n };\n\n/**\n * Attempts an unload-safe action call without exceeding the browser's shared\n * keepalive request budget. The reservation remains held until the response\n * body has completed, because browsers count every in-flight keepalive body\n * against the same per-document quota.\n *\n * A rejected attempt is deliberately synchronous so callers can fall back to\n * a durable outbox before returning from `pagehide`.\n */\nexport function tryCallActionKeepalive<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options: Omit<ClientActionCallOptions, \"method\"> = {},\n): KeepaliveActionCallResult<\n TResult extends undefined ? ActionResult<TName> : TResult\n> {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n const serializedBody = JSON.stringify(params ?? {});\n const bodyBytes = utf8ByteLength(serializedBody);\n\n if (bodyBytes > ACTION_KEEPALIVE_BODY_BUDGET_BYTES) {\n return {\n accepted: false,\n bodyBytes,\n reason: \"body-too-large\",\n completion: null,\n };\n }\n\n if (\n reservedKeepaliveBodyBytes + bodyBytes >\n ACTION_KEEPALIVE_BODY_BUDGET_BYTES\n ) {\n return {\n accepted: false,\n bodyBytes,\n reason: \"budget-exhausted\",\n completion: null,\n };\n }\n\n reservedKeepaliveBodyBytes += bodyBytes;\n const completion = actionFetch<R>(actionName, \"POST\", params, {\n signal: options.signal,\n timeoutMs: options.timeoutMs,\n keepalive: true,\n serializedBody,\n }).finally(() => {\n reservedKeepaliveBodyBytes = Math.max(\n 0,\n reservedKeepaliveBodyBytes - bodyBytes,\n );\n });\n\n return { accepted: true, bodyBytes, completion };\n}\n\n// ---------------------------------------------------------------------------\n// Query hook\n// ---------------------------------------------------------------------------\n\n/**\n * Query an action exposed as GET.\n *\n * When the action type registry is generated, the return type and parameter\n * types are inferred automatically from the action's `defineAction()` call.\n *\n * ```ts\n * // Type-safe — no manual generic needed\n * const { data } = useActionQuery(\"list-meals\", { date: \"2025-01-01\" });\n *\n * // Manual override still works when needed\n * const { data } = useActionQuery<CustomType>(\"list-meals\");\n * ```\n */\nexport function useActionQuery<\n TResult = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n params?: ActionParams<TName>,\n options?: Omit<\n UseQueryOptions<TResult extends undefined ? ActionResult<TName> : TResult>,\n \"queryKey\" | \"queryFn\"\n >,\n) {\n type R = TResult extends undefined ? ActionResult<TName> : TResult;\n return useQuery<R>({\n queryKey: [\"action\", actionName, params],\n // Thread React Query's per-fetch AbortSignal into the network request so\n // superseded fetches (key change, unmount, rapid refetch) actually cancel\n // instead of holding a per-origin connection slot until they finish.\n queryFn: ({ signal }) =>\n actionFetch<R>(actionName, \"GET\", params, { signal }),\n retry: defaultActionQueryRetry,\n retryDelay: defaultActionQueryRetryDelay,\n ...options,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Mutation hook\n// ---------------------------------------------------------------------------\n\n/**\n * Mutate via an action exposed as POST (default), PUT, or DELETE.\n *\n * When the action type registry is generated, the return type and parameter\n * types are inferred automatically.\n *\n * ```ts\n * // Type-safe\n * const { mutate } = useActionMutation(\"log-meal\");\n * mutate({ name: \"Salad\", calories: 350 });\n * ```\n */\nexport function useActionMutation<\n TData = undefined,\n TVariables = undefined,\n TName extends ActionName = ActionName,\n>(\n actionName: TName,\n options?: Omit<\n UseMutationOptions<\n TData extends undefined ? ActionResult<TName> : TData,\n Error,\n TVariables extends undefined ? ActionParams<TName> : TVariables\n >,\n \"mutationFn\"\n > & {\n method?: \"POST\" | \"PUT\" | \"DELETE\";\n skipActionQueryInvalidation?: boolean;\n },\n) {\n const queryClient = useQueryClient();\n const {\n method: methodOpt,\n onSuccess,\n skipActionQueryInvalidation = false,\n ...restOptions\n } = options ?? ({} as any);\n const method = methodOpt ?? \"POST\";\n\n type D = TData extends undefined ? ActionResult<TName> : TData;\n type V = TVariables extends undefined ? ActionParams<TName> : TVariables;\n\n return useMutation<D, Error, V>({\n ...restOptions,\n mutationFn: (params) =>\n actionFetch<D>(actionName, method, params as Record<string, any>),\n onSuccess: (...args: [any, any, any]) => {\n // Most mutations change app data broadly. High-volume background\n // mutations can opt out and perform narrower invalidation in onSuccess.\n if (!skipActionQueryInvalidation) {\n queryClient.invalidateQueries({ queryKey: [\"action\"] });\n }\n (onSuccess as Function)?.(...args);\n },\n });\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"use-session.d.ts","sourceRoot":"","sources":["../../src/client/use-session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,UAAU,gBAAgB;IACxB,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CA4D7C"}
1
+ {"version":3,"file":"use-session.d.ts","sourceRoot":"","sources":["../../src/client/use-session.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIrD,YAAY,EAAE,WAAW,EAAE,CAAC;AAE5B,UAAU,gBAAgB;IACxB,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;CACpB;AA6DD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,IAAI,gBAAgB,CAgC7C"}
@@ -1,6 +1,58 @@
1
- import { useState, useEffect, useRef } from "react";
1
+ import { useEffect, useState } from "react";
2
2
  import { setSentryUser, trackSessionStatus } from "./analytics.js";
3
3
  import { agentNativePath } from "./api-path.js";
4
+ const SESSION_CACHE_TTL_MS = 30_000;
5
+ const SESSION_RETRY_DELAY_MS = 1_000;
6
+ let cachedSession;
7
+ let cachedSessionAt = 0;
8
+ let sessionRequest;
9
+ let trackedSessionIdentity;
10
+ function hasFreshSessionCache() {
11
+ return (cachedSession !== undefined &&
12
+ Date.now() - cachedSessionAt < SESSION_CACHE_TTL_MS);
13
+ }
14
+ function publishSessionIdentity(session) {
15
+ const identity = session?.userId ?? session?.email ?? null;
16
+ if (trackedSessionIdentity !== identity) {
17
+ trackedSessionIdentity = identity;
18
+ if (session) {
19
+ setSentryUser({
20
+ id: session.userId,
21
+ email: session.email,
22
+ username: session.name,
23
+ }, session.orgId ?? null);
24
+ }
25
+ else {
26
+ setSentryUser(null, null);
27
+ }
28
+ }
29
+ trackSessionStatus(Boolean(session));
30
+ }
31
+ function fetchSharedSession() {
32
+ if (hasFreshSessionCache())
33
+ return Promise.resolve(cachedSession ?? null);
34
+ if (sessionRequest)
35
+ return sessionRequest;
36
+ sessionRequest = (async () => {
37
+ try {
38
+ const res = await fetch(agentNativePath("/_agent-native/auth/session"));
39
+ if (!res.ok)
40
+ return undefined;
41
+ const data = await res.json();
42
+ const session = data.error ? null : data;
43
+ cachedSession = session;
44
+ cachedSessionAt = Date.now();
45
+ publishSessionIdentity(session);
46
+ return session;
47
+ }
48
+ catch {
49
+ return undefined;
50
+ }
51
+ })().finally(() => {
52
+ sessionRequest = undefined;
53
+ });
54
+ return sessionRequest;
55
+ }
4
56
  /**
5
57
  * Client-side hook to get the current auth session.
6
58
  *
@@ -11,60 +63,30 @@ import { agentNativePath } from "./api-path.js";
11
63
  * Templates should use this instead of building their own auth context.
12
64
  */
13
65
  export function useSession() {
14
- const [session, setSession] = useState(null);
15
- const [isLoading, setIsLoading] = useState(true);
16
- const trackedRef = useRef(false);
66
+ const cached = hasFreshSessionCache() ? (cachedSession ?? null) : null;
67
+ const [session, setSession] = useState(cached);
68
+ const [isLoading, setIsLoading] = useState(!hasFreshSessionCache());
17
69
  useEffect(() => {
18
70
  let cancelled = false;
19
- async function fetchSession() {
20
- let signedIn = false;
21
- let resolved = null;
22
- try {
23
- const res = await fetch(agentNativePath("/_agent-native/auth/session"));
24
- if (!res.ok) {
25
- setSession(null);
26
- return;
27
- }
28
- const data = await res.json();
29
- if (!cancelled) {
30
- // The endpoint returns { error: "..." } when not authenticated
31
- if (data.error) {
32
- setSession(null);
33
- }
34
- else {
35
- resolved = data;
36
- setSession(resolved);
37
- signedIn = true;
38
- }
39
- }
40
- }
41
- catch {
42
- if (!cancelled)
43
- setSession(null);
44
- }
45
- finally {
46
- if (!cancelled) {
47
- setIsLoading(false);
48
- if (resolved) {
49
- setSentryUser({
50
- id: resolved.userId,
51
- email: resolved.email,
52
- username: resolved.name,
53
- }, resolved.orgId ?? null);
54
- }
55
- else {
56
- setSentryUser(null, null);
57
- }
58
- if (!trackedRef.current) {
59
- trackedRef.current = true;
60
- trackSessionStatus(signedIn);
61
- }
62
- }
71
+ let retryTimer;
72
+ const resolveSession = async () => {
73
+ const resolved = await fetchSharedSession();
74
+ if (cancelled)
75
+ return;
76
+ if (resolved === undefined) {
77
+ retryTimer = setTimeout(() => {
78
+ void resolveSession();
79
+ }, SESSION_RETRY_DELAY_MS);
80
+ return;
63
81
  }
64
- }
65
- fetchSession();
82
+ setSession(resolved);
83
+ setIsLoading(false);
84
+ };
85
+ void resolveSession();
66
86
  return () => {
67
87
  cancelled = true;
88
+ if (retryTimer)
89
+ clearTimeout(retryTimer);
68
90
  };
69
91
  }, []);
70
92
  return { session, isLoading };
@@ -1 +1 @@
1
- {"version":3,"file":"use-session.js","sourceRoot":"","sources":["../../src/client/use-session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAGpD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAShD;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU;IACxB,MAAM,CAAC,OAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEjC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,KAAK,UAAU,YAAY;YACzB,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,IAAI,QAAQ,GAAuB,IAAI,CAAC;YACxC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC,6BAA6B,CAAC,CAAC,CAAC;gBACxE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,UAAU,CAAC,IAAI,CAAC,CAAC;oBACjB,OAAO;gBACT,CAAC;gBACD,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;gBAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,+DAA+D;oBAC/D,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;wBACf,UAAU,CAAC,IAAI,CAAC,CAAC;oBACnB,CAAC;yBAAM,CAAC;wBACN,QAAQ,GAAG,IAAmB,CAAC;wBAC/B,UAAU,CAAC,QAAQ,CAAC,CAAC;wBACrB,QAAQ,GAAG,IAAI,CAAC;oBAClB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,IAAI,CAAC,SAAS;oBAAE,UAAU,CAAC,IAAI,CAAC,CAAC;YACnC,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,SAAS,EAAE,CAAC;oBACf,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,IAAI,QAAQ,EAAE,CAAC;wBACb,aAAa,CACX;4BACE,EAAE,EAAE,QAAQ,CAAC,MAAM;4BACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;4BACrB,QAAQ,EAAE,QAAQ,CAAC,IAAI;yBACxB,EACD,QAAQ,CAAC,KAAK,IAAI,IAAI,CACvB,CAAC;oBACJ,CAAC;yBAAM,CAAC;wBACN,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;oBAC5B,CAAC;oBACD,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;wBACxB,UAAU,CAAC,OAAO,GAAG,IAAI,CAAC;wBAC1B,kBAAkB,CAAC,QAAQ,CAAC,CAAC;oBAC/B,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,YAAY,EAAE,CAAC;QACf,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;QACnB,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;AAChC,CAAC","sourcesContent":["import { useState, useEffect, useRef } from \"react\";\n\nimport type { AuthSession } from \"../server/auth.js\";\nimport { setSentryUser, trackSessionStatus } from \"./analytics.js\";\nimport { agentNativePath } from \"./api-path.js\";\n\nexport type { AuthSession };\n\ninterface UseSessionResult {\n session: AuthSession | null;\n isLoading: boolean;\n}\n\n/**\n * Client-side hook to get the current auth session.\n *\n * Fetches the current session from `/_agent-native/auth/session` and returns\n * it, or `null` when unauthenticated. This behavior is the same in all\n * environments — there is no dev bypass and no `local@localhost` sentinel.\n *\n * Templates should use this instead of building their own auth context.\n */\nexport function useSession(): UseSessionResult {\n const [session, setSession] = useState<AuthSession | null>(null);\n const [isLoading, setIsLoading] = useState(true);\n const trackedRef = useRef(false);\n\n useEffect(() => {\n let cancelled = false;\n\n async function fetchSession() {\n let signedIn = false;\n let resolved: AuthSession | null = null;\n try {\n const res = await fetch(agentNativePath(\"/_agent-native/auth/session\"));\n if (!res.ok) {\n setSession(null);\n return;\n }\n const data = await res.json();\n if (!cancelled) {\n // The endpoint returns { error: \"...\" } when not authenticated\n if (data.error) {\n setSession(null);\n } else {\n resolved = data as AuthSession;\n setSession(resolved);\n signedIn = true;\n }\n }\n } catch {\n if (!cancelled) setSession(null);\n } finally {\n if (!cancelled) {\n setIsLoading(false);\n if (resolved) {\n setSentryUser(\n {\n id: resolved.userId,\n email: resolved.email,\n username: resolved.name,\n },\n resolved.orgId ?? null,\n );\n } else {\n setSentryUser(null, null);\n }\n if (!trackedRef.current) {\n trackedRef.current = true;\n trackSessionStatus(signedIn);\n }\n }\n }\n }\n\n fetchSession();\n return () => {\n cancelled = true;\n };\n }, []);\n\n return { session, isLoading };\n}\n"]}
1
+ {"version":3,"file":"use-session.js","sourceRoot":"","sources":["../../src/client/use-session.ts"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useState } from \"react\";\n\nimport type { AuthSession } from \"../server/auth.js\";\nimport { setSentryUser, trackSessionStatus } from \"./analytics.js\";\nimport { agentNativePath } from \"./api-path.js\";\n\nexport type { AuthSession };\n\ninterface UseSessionResult {\n session: AuthSession | null;\n isLoading: boolean;\n}\n\nconst SESSION_CACHE_TTL_MS = 30_000;\nconst SESSION_RETRY_DELAY_MS = 1_000;\nlet cachedSession: AuthSession | null | undefined;\nlet cachedSessionAt = 0;\nlet sessionRequest: Promise<AuthSession | null | undefined> | undefined;\nlet trackedSessionIdentity: string | null | undefined;\n\nfunction hasFreshSessionCache(): boolean {\n return (\n cachedSession !== undefined &&\n Date.now() - cachedSessionAt < SESSION_CACHE_TTL_MS\n );\n}\n\nfunction publishSessionIdentity(session: AuthSession | null): void {\n const identity = session?.userId ?? session?.email ?? null;\n if (trackedSessionIdentity !== identity) {\n trackedSessionIdentity = identity;\n if (session) {\n setSentryUser(\n {\n id: session.userId,\n email: session.email,\n username: session.name,\n },\n session.orgId ?? null,\n );\n } else {\n setSentryUser(null, null);\n }\n }\n trackSessionStatus(Boolean(session));\n}\n\nfunction fetchSharedSession(): Promise<AuthSession | null | undefined> {\n if (hasFreshSessionCache()) return Promise.resolve(cachedSession ?? null);\n if (sessionRequest) return sessionRequest;\n\n sessionRequest = (async () => {\n try {\n const res = await fetch(agentNativePath(\"/_agent-native/auth/session\"));\n if (!res.ok) return undefined;\n\n const data = await res.json();\n const session = data.error ? null : (data as AuthSession);\n cachedSession = session;\n cachedSessionAt = Date.now();\n publishSessionIdentity(session);\n return session;\n } catch {\n return undefined;\n }\n })().finally(() => {\n sessionRequest = undefined;\n });\n\n return sessionRequest;\n}\n\n/**\n * Client-side hook to get the current auth session.\n *\n * Fetches the current session from `/_agent-native/auth/session` and returns\n * it, or `null` when unauthenticated. This behavior is the same in all\n * environments — there is no dev bypass and no `local@localhost` sentinel.\n *\n * Templates should use this instead of building their own auth context.\n */\nexport function useSession(): UseSessionResult {\n const cached = hasFreshSessionCache() ? (cachedSession ?? null) : null;\n const [session, setSession] = useState<AuthSession | null>(cached);\n const [isLoading, setIsLoading] = useState(!hasFreshSessionCache());\n\n useEffect(() => {\n let cancelled = false;\n let retryTimer: ReturnType<typeof setTimeout> | undefined;\n\n const resolveSession = async () => {\n const resolved = await fetchSharedSession();\n if (cancelled) return;\n\n if (resolved === undefined) {\n retryTimer = setTimeout(() => {\n void resolveSession();\n }, SESSION_RETRY_DELAY_MS);\n return;\n }\n\n setSession(resolved);\n setIsLoading(false);\n };\n\n void resolveSession();\n return () => {\n cancelled = true;\n if (retryTimer) clearTimeout(retryTimer);\n };\n }, []);\n\n return { session, isLoading };\n}\n"]}
@@ -26,8 +26,8 @@ export declare const getCollabState: import("h3").EventHandlerWithFetch<import("
26
26
  * Body: { update: string (base64), requestSource?: string }
27
27
  */
28
28
  export declare const postCollabUpdate: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
29
- ok?: undefined;
30
29
  error: string;
30
+ ok?: undefined;
31
31
  } | {
32
32
  error?: undefined;
33
33
  ok: boolean;
@@ -13,8 +13,8 @@
13
13
  * Body: { json: any, fieldName?: string, type?: "map"|"array", requestSource?: string }
14
14
  */
15
15
  export declare const postCollabJson: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
16
- error: string;
17
16
  ok?: undefined;
17
+ error: string;
18
18
  } | {
19
19
  error?: undefined;
20
20
  ok: boolean;