@athenaintel/react 0.27.0 → 0.28.0

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/README.md +17 -1
  2. package/dist/chat/StatewireApprovalCard.cjs +19 -0
  3. package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
  4. package/dist/chat/StatewireApprovalCard.js +19 -0
  5. package/dist/chat/StatewireApprovalCard.js.map +1 -1
  6. package/dist/chat/message-time.cjs.map +1 -1
  7. package/dist/chat/message-time.d.ts +4 -3
  8. package/dist/chat/message-time.js.map +1 -1
  9. package/dist/composer/QuoteContext.cjs +13 -1
  10. package/dist/composer/QuoteContext.cjs.map +1 -1
  11. package/dist/composer/QuoteContext.d.ts +11 -1
  12. package/dist/composer/QuoteContext.js +13 -1
  13. package/dist/composer/QuoteContext.js.map +1 -1
  14. package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
  15. package/dist/diagnostics/athena-diagnostics.d.ts +1 -1
  16. package/dist/diagnostics/athena-diagnostics.js.map +1 -1
  17. package/dist/lib/athena-urls.cjs +1 -1
  18. package/dist/lib/athena-urls.cjs.map +1 -1
  19. package/dist/lib/athena-urls.js +1 -1
  20. package/dist/lib/athena-urls.js.map +1 -1
  21. package/dist/lib/posthog/mask-sensitive-input.cjs +34 -0
  22. package/dist/lib/posthog/mask-sensitive-input.cjs.map +1 -0
  23. package/dist/lib/posthog/mask-sensitive-input.d.ts +9 -0
  24. package/dist/lib/posthog/mask-sensitive-input.js +34 -0
  25. package/dist/lib/posthog/mask-sensitive-input.js.map +1 -0
  26. package/dist/packages/agent-runtime/src/conversation-index.cjs +2 -1
  27. package/dist/packages/agent-runtime/src/conversation-index.cjs.map +1 -1
  28. package/dist/packages/agent-runtime/src/conversation-index.js +2 -1
  29. package/dist/packages/agent-runtime/src/conversation-index.js.map +1 -1
  30. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs +2 -0
  31. package/dist/packages/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
  32. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js +2 -0
  33. package/dist/packages/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
  34. package/dist/packages/agent-runtime/src/interrupts.cjs +26 -8
  35. package/dist/packages/agent-runtime/src/interrupts.cjs.map +1 -1
  36. package/dist/packages/agent-runtime/src/interrupts.js +26 -8
  37. package/dist/packages/agent-runtime/src/interrupts.js.map +1 -1
  38. package/dist/packages/agent-runtime/src/messages.cjs +70 -14
  39. package/dist/packages/agent-runtime/src/messages.cjs.map +1 -1
  40. package/dist/packages/agent-runtime/src/messages.js +71 -15
  41. package/dist/packages/agent-runtime/src/messages.js.map +1 -1
  42. package/dist/packages/agent-runtime/src/tool-metadata.cjs +75 -0
  43. package/dist/packages/agent-runtime/src/tool-metadata.cjs.map +1 -1
  44. package/dist/packages/agent-runtime/src/tool-metadata.js +75 -0
  45. package/dist/packages/agent-runtime/src/tool-metadata.js.map +1 -1
  46. package/dist/packages/agent-runtime/src/tool-result.cjs +19 -4
  47. package/dist/packages/agent-runtime/src/tool-result.cjs.map +1 -1
  48. package/dist/packages/agent-runtime/src/tool-result.js +19 -4
  49. package/dist/packages/agent-runtime/src/tool-result.js.map +1 -1
  50. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs +23 -4
  51. package/dist/packages/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
  52. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js +24 -5
  53. package/dist/packages/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
  54. package/dist/packages/react/package.json.cjs +1 -1
  55. package/dist/packages/react/package.json.js +1 -1
  56. package/dist/provider/PostHogProvider.cjs +3 -1
  57. package/dist/provider/PostHogProvider.cjs.map +1 -1
  58. package/dist/provider/PostHogProvider.js +3 -1
  59. package/dist/provider/PostHogProvider.js.map +1 -1
  60. package/dist/runtime/auth.cjs +88 -4
  61. package/dist/runtime/auth.cjs.map +1 -1
  62. package/dist/runtime/auth.d.ts +72 -5
  63. package/dist/runtime/auth.js +88 -4
  64. package/dist/runtime/auth.js.map +1 -1
  65. package/dist/styles.css +1 -1
  66. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"QuoteContext.cjs","sources":["../../src/composer/QuoteContext.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nexport interface QuoteData {\n /** The selected/quoted text. */\n text: string;\n /** Title of the source (e.g., asset title, document name). */\n sourceTitle?: string;\n /** Asset ID of the source (for attribution). */\n sourceAssetId?: string;\n /** Message ID of the source message (when quoting from chat). */\n sourceMessageId?: string;\n /** Role of the source message ('assistant' | 'user'). */\n sourceRole?: string;\n}\n\ninterface QuoteContextValue {\n quote: QuoteData | null;\n setQuote: (quote: QuoteData | null) => void;\n clearQuote: () => void;\n}\n\nconst QuoteCtx = createContext<QuoteContextValue | null>(null);\n\nexport function QuoteProvider({ children }: { children: ReactNode }) {\n const [quote, setQuote] = useState<QuoteData | null>(null);\n const clearQuote = useCallback(() => setQuote(null), []);\n\n const value = useMemo(() => ({ quote, setQuote, clearQuote }), [quote, setQuote, clearQuote]);\n\n return (\n <QuoteCtx.Provider value={value}>\n {children}\n </QuoteCtx.Provider>\n );\n}\n\nexport function useQuote(): QuoteContextValue {\n const ctx = useContext(QuoteCtx);\n if (!ctx) {\n throw new Error('[AthenaSDK] useQuote must be used within <QuoteProvider>');\n }\n return ctx;\n}\n\n/**\n * Listens for:\n * 1. Cmd+L / Ctrl+L keydown — grabs the current text selection and sets it as a quote.\n * `preventDefault` stops the browser from focusing the URL bar.\n * 2. PostMessage events from child iframes (asset panels, embedded content)\n * with type `athena-add-to-composer`.\n *\n * Mount this once inside AthenaProvider.\n */\nexport function useQuoteFromPostMessage() {\n const { setQuote } = useQuote();\n\n useEffect(() => {\n // ── Cmd+L / Ctrl+L keyboard handler ──────────────────────────────\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n const sel = window.getSelection();\n const selection = sel?.toString()?.trim();\n\n if (selection && selection.length > 0) {\n e.preventDefault();\n\n // Walk up from the selection anchor to find the message container.\n // assistant-ui message roots have data-role=\"assistant\" or data-role=\"user\".\n let messageId: string | undefined;\n let role: string | undefined;\n let node = sel?.anchorNode as HTMLElement | null;\n while (node && node !== document.body) {\n if (node instanceof HTMLElement) {\n const dataRole = node.getAttribute('data-role');\n if (dataRole === 'assistant' || dataRole === 'user') {\n role = dataRole;\n // assistant-ui sets data-message-id on message roots\n messageId = node.getAttribute('data-message-id') ?? undefined;\n break;\n }\n }\n node = node.parentElement;\n }\n\n setQuote({ text: selection, sourceRole: role, sourceMessageId: messageId });\n }\n }\n };\n\n // ── PostMessage handler (from iframes) ───────────────────────────\n // NOTE: Accepts messages from any origin by design. The SDK consumer\n // controls which iframes are embedded, defining the trust boundary.\n // A future `allowedOrigins` config could restrict this if needed.\n const handleMessage = (event: MessageEvent) => {\n const data = event.data;\n if (\n data &&\n typeof data === 'object' &&\n data.type === 'athena-add-to-composer' &&\n typeof data.text === 'string' &&\n data.text.trim().length > 0\n ) {\n setQuote({\n text: data.text,\n sourceTitle: data.sourceTitle,\n sourceAssetId: data.sourceAssetId,\n });\n }\n };\n\n document.addEventListener('keydown', handleKeydown, true); // capture phase to beat the browser\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('keydown', handleKeydown, true);\n window.removeEventListener('message', handleMessage);\n };\n }, [setQuote]);\n}\n"],"names":["createContext","useState","useCallback","useMemo","jsx","useContext","useEffect"],"mappings":";;;;AAqBA,MAAM,WAAWA,MAAAA,cAAwC,IAAI;AAEtD,SAAS,cAAc,EAAE,YAAqC;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAIC,MAAAA,SAA2B,IAAI;AACzD,QAAM,aAAaC,MAAAA,YAAY,MAAM,SAAS,IAAI,GAAG,CAAA,CAAE;AAEvD,QAAM,QAAQC,MAAAA,QAAQ,OAAO,EAAE,OAAO,UAAU,WAAA,IAAe,CAAC,OAAO,UAAU,UAAU,CAAC;AAE5F,SACEC,2BAAAA,IAAC,SAAS,UAAT,EAAkB,OAChB,SAAA,CACH;AAEJ;AAEO,SAAS,WAA8B;AAC5C,QAAM,MAAMC,MAAAA,WAAW,QAAQ;AAC/B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;AAWO,SAAS,0BAA0B;AACxC,QAAM,EAAE,SAAA,IAAa,SAAA;AAErBC,QAAAA,UAAU,MAAM;AAEd,UAAM,gBAAgB,CAAC,MAAqB;;AAC1C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAA,MAAkB,KAAK;AAC3D,cAAM,MAAM,OAAO,aAAA;AACnB,cAAM,aAAY,gCAAK,eAAL,mBAAiB;AAEnC,YAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAE,eAAA;AAIF,cAAI;AACJ,cAAI;AACJ,cAAI,OAAO,2BAAK;AAChB,iBAAO,QAAQ,SAAS,SAAS,MAAM;AACrC,gBAAI,gBAAgB,aAAa;AAC/B,oBAAM,WAAW,KAAK,aAAa,WAAW;AAC9C,kBAAI,aAAa,eAAe,aAAa,QAAQ;AACnD,uBAAO;AAEP,4BAAY,KAAK,aAAa,iBAAiB,KAAK;AACpD;AAAA,cACF;AAAA,YACF;AACA,mBAAO,KAAK;AAAA,UACd;AAEA,mBAAS,EAAE,MAAM,WAAW,YAAY,MAAM,iBAAiB,WAAW;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAMA,UAAM,gBAAgB,CAAC,UAAwB;AAC7C,YAAM,OAAO,MAAM;AACnB,UACE,QACA,OAAO,SAAS,YAChB,KAAK,SAAS,4BACd,OAAO,KAAK,SAAS,YACrB,KAAK,KAAK,KAAA,EAAO,SAAS,GAC1B;AACA,iBAAS;AAAA,UACP,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,QAAA,CACrB;AAAA,MACH;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,iBAAiB,WAAW,aAAa;AAEhD,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAC3D,aAAO,oBAAoB,WAAW,aAAa;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACf;;;;"}
1
+ {"version":3,"file":"QuoteContext.cjs","sources":["../../src/composer/QuoteContext.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nexport interface QuoteData {\n /** The selected/quoted text. */\n text: string;\n /** Title of the source (e.g., asset title, document name). */\n sourceTitle?: string;\n /** Asset ID of the source (for attribution). */\n sourceAssetId?: string;\n /** Message ID of the source message (when quoting from chat). */\n sourceMessageId?: string;\n /** Role of the source message ('assistant' | 'user'). */\n sourceRole?: string;\n}\n\ninterface QuoteContextValue {\n quote: QuoteData | null;\n setQuote: (quote: QuoteData | null) => void;\n clearQuote: () => void;\n}\n\nconst QuoteCtx = createContext<QuoteContextValue | null>(null);\n\nexport function QuoteProvider({ children }: { children: ReactNode }) {\n const [quote, setQuote] = useState<QuoteData | null>(null);\n const clearQuote = useCallback(() => setQuote(null), []);\n\n const value = useMemo(() => ({ quote, setQuote, clearQuote }), [quote, setQuote, clearQuote]);\n\n return (\n <QuoteCtx.Provider value={value}>\n {children}\n </QuoteCtx.Provider>\n );\n}\n\nexport function useQuote(): QuoteContextValue {\n const ctx = useContext(QuoteCtx);\n if (!ctx) {\n throw new Error('[AthenaSDK] useQuote must be used within <QuoteProvider>');\n }\n return ctx;\n}\n\n/**\n * Whether a message came from a frame embedded directly in this window, for\n * any origin: the window of one of this document's iframes (an identity\n * comparison, safe cross-origin), or, for a frame inside a shadow root, a\n * window whose `parent` is this window (`parent` is readable on a\n * cross-origin window). A message port, a worker, this window itself, a\n * grandchild frame or an unrelated window is refused.\n */\nexport function isDirectChildFrame(source: MessageEventSource | null): boolean {\n if (!source || source === window) return false;\n for (const frame of document.querySelectorAll('iframe')) {\n if (frame.contentWindow === source) return true;\n }\n try {\n return Reflect.get(source, 'parent') === window;\n } catch {\n return false;\n }\n}\n\n/**\n * Listens for:\n * 1. Cmd+L / Ctrl+L keydown — grabs the current text selection and sets it as a quote.\n * `preventDefault` stops the browser from focusing the URL bar.\n * 2. PostMessage events from child iframes (asset panels, embedded content)\n * with type `athena-add-to-composer`, only from a frame embedded directly\n * in this window ({@link isDirectChildFrame}).\n *\n * Mount this once inside AthenaProvider.\n */\nexport function useQuoteFromPostMessage() {\n const { setQuote } = useQuote();\n\n useEffect(() => {\n // ── Cmd+L / Ctrl+L keyboard handler ──────────────────────────────\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n const sel = window.getSelection();\n const selection = sel?.toString()?.trim();\n\n if (selection && selection.length > 0) {\n e.preventDefault();\n\n // Walk up from the selection anchor to find the message container.\n // assistant-ui message roots have data-role=\"assistant\" or data-role=\"user\".\n let messageId: string | undefined;\n let role: string | undefined;\n let node = sel?.anchorNode as HTMLElement | null;\n while (node && node !== document.body) {\n if (node instanceof HTMLElement) {\n const dataRole = node.getAttribute('data-role');\n if (dataRole === 'assistant' || dataRole === 'user') {\n role = dataRole;\n // assistant-ui sets data-message-id on message roots\n messageId = node.getAttribute('data-message-id') ?? undefined;\n break;\n }\n }\n node = node.parentElement;\n }\n\n setQuote({ text: selection, sourceRole: role, sourceMessageId: messageId });\n }\n }\n };\n\n // ── PostMessage handler (from iframes) ───────────────────────────\n // Accepts messages from any origin, but only from a frame embedded\n // directly in this window: the SDK consumer controls which iframes are\n // embedded, and that is the trust boundary. A popup, an opener or another\n // tab holding a reference to this window cannot stage composer text.\n const handleMessage = (event: MessageEvent) => {\n const data = event.data;\n if (\n data &&\n typeof data === 'object' &&\n data.type === 'athena-add-to-composer' &&\n typeof data.text === 'string' &&\n data.text.trim().length > 0 &&\n isDirectChildFrame(event.source)\n ) {\n setQuote({\n text: data.text,\n sourceTitle: data.sourceTitle,\n sourceAssetId: data.sourceAssetId,\n });\n }\n };\n\n document.addEventListener('keydown', handleKeydown, true); // capture phase to beat the browser\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('keydown', handleKeydown, true);\n window.removeEventListener('message', handleMessage);\n };\n }, [setQuote]);\n}\n"],"names":["createContext","useState","useCallback","useMemo","jsx","useContext","useEffect"],"mappings":";;;;AAqBA,MAAM,WAAWA,MAAAA,cAAwC,IAAI;AAEtD,SAAS,cAAc,EAAE,YAAqC;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAIC,MAAAA,SAA2B,IAAI;AACzD,QAAM,aAAaC,MAAAA,YAAY,MAAM,SAAS,IAAI,GAAG,CAAA,CAAE;AAEvD,QAAM,QAAQC,MAAAA,QAAQ,OAAO,EAAE,OAAO,UAAU,WAAA,IAAe,CAAC,OAAO,UAAU,UAAU,CAAC;AAE5F,SACEC,2BAAAA,IAAC,SAAS,UAAT,EAAkB,OAChB,SAAA,CACH;AAEJ;AAEO,SAAS,WAA8B;AAC5C,QAAM,MAAMC,MAAAA,WAAW,QAAQ;AAC/B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,QAA4C;AAC7E,MAAI,CAAC,UAAU,WAAW,OAAQ,QAAO;AACzC,aAAW,SAAS,SAAS,iBAAiB,QAAQ,GAAG;AACvD,QAAI,MAAM,kBAAkB,OAAQ,QAAO;AAAA,EAC7C;AACA,MAAI;AACF,WAAO,QAAQ,IAAI,QAAQ,QAAQ,MAAM;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,0BAA0B;AACxC,QAAM,EAAE,SAAA,IAAa,SAAA;AAErBC,QAAAA,UAAU,MAAM;AAEd,UAAM,gBAAgB,CAAC,MAAqB;;AAC1C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAA,MAAkB,KAAK;AAC3D,cAAM,MAAM,OAAO,aAAA;AACnB,cAAM,aAAY,gCAAK,eAAL,mBAAiB;AAEnC,YAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAE,eAAA;AAIF,cAAI;AACJ,cAAI;AACJ,cAAI,OAAO,2BAAK;AAChB,iBAAO,QAAQ,SAAS,SAAS,MAAM;AACrC,gBAAI,gBAAgB,aAAa;AAC/B,oBAAM,WAAW,KAAK,aAAa,WAAW;AAC9C,kBAAI,aAAa,eAAe,aAAa,QAAQ;AACnD,uBAAO;AAEP,4BAAY,KAAK,aAAa,iBAAiB,KAAK;AACpD;AAAA,cACF;AAAA,YACF;AACA,mBAAO,KAAK;AAAA,UACd;AAEA,mBAAS,EAAE,MAAM,WAAW,YAAY,MAAM,iBAAiB,WAAW;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAOA,UAAM,gBAAgB,CAAC,UAAwB;AAC7C,YAAM,OAAO,MAAM;AACnB,UACE,QACA,OAAO,SAAS,YAChB,KAAK,SAAS,4BACd,OAAO,KAAK,SAAS,YACrB,KAAK,KAAK,OAAO,SAAS,KAC1B,mBAAmB,MAAM,MAAM,GAC/B;AACA,iBAAS;AAAA,UACP,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,QAAA,CACrB;AAAA,MACH;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,iBAAiB,WAAW,aAAa;AAEhD,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAC3D,aAAO,oBAAoB,WAAW,aAAa;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACf;;;;;"}
@@ -20,12 +20,22 @@ export declare function QuoteProvider({ children }: {
20
20
  children: ReactNode;
21
21
  }): import("react").JSX.Element;
22
22
  export declare function useQuote(): QuoteContextValue;
23
+ /**
24
+ * Whether a message came from a frame embedded directly in this window, for
25
+ * any origin: the window of one of this document's iframes (an identity
26
+ * comparison, safe cross-origin), or, for a frame inside a shadow root, a
27
+ * window whose `parent` is this window (`parent` is readable on a
28
+ * cross-origin window). A message port, a worker, this window itself, a
29
+ * grandchild frame or an unrelated window is refused.
30
+ */
31
+ export declare function isDirectChildFrame(source: MessageEventSource | null): boolean;
23
32
  /**
24
33
  * Listens for:
25
34
  * 1. Cmd+L / Ctrl+L keydown — grabs the current text selection and sets it as a quote.
26
35
  * `preventDefault` stops the browser from focusing the URL bar.
27
36
  * 2. PostMessage events from child iframes (asset panels, embedded content)
28
- * with type `athena-add-to-composer`.
37
+ * with type `athena-add-to-composer`, only from a frame embedded directly
38
+ * in this window ({@link isDirectChildFrame}).
29
39
  *
30
40
  * Mount this once inside AthenaProvider.
31
41
  */
@@ -14,6 +14,17 @@ function useQuote() {
14
14
  }
15
15
  return ctx;
16
16
  }
17
+ function isDirectChildFrame(source) {
18
+ if (!source || source === window) return false;
19
+ for (const frame of document.querySelectorAll("iframe")) {
20
+ if (frame.contentWindow === source) return true;
21
+ }
22
+ try {
23
+ return Reflect.get(source, "parent") === window;
24
+ } catch {
25
+ return false;
26
+ }
27
+ }
17
28
  function useQuoteFromPostMessage() {
18
29
  const { setQuote } = useQuote();
19
30
  useEffect(() => {
@@ -44,7 +55,7 @@ function useQuoteFromPostMessage() {
44
55
  };
45
56
  const handleMessage = (event) => {
46
57
  const data = event.data;
47
- if (data && typeof data === "object" && data.type === "athena-add-to-composer" && typeof data.text === "string" && data.text.trim().length > 0) {
58
+ if (data && typeof data === "object" && data.type === "athena-add-to-composer" && typeof data.text === "string" && data.text.trim().length > 0 && isDirectChildFrame(event.source)) {
48
59
  setQuote({
49
60
  text: data.text,
50
61
  sourceTitle: data.sourceTitle,
@@ -62,6 +73,7 @@ function useQuoteFromPostMessage() {
62
73
  }
63
74
  export {
64
75
  QuoteProvider,
76
+ isDirectChildFrame,
65
77
  useQuote,
66
78
  useQuoteFromPostMessage
67
79
  };
@@ -1 +1 @@
1
- {"version":3,"file":"QuoteContext.js","sources":["../../src/composer/QuoteContext.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nexport interface QuoteData {\n /** The selected/quoted text. */\n text: string;\n /** Title of the source (e.g., asset title, document name). */\n sourceTitle?: string;\n /** Asset ID of the source (for attribution). */\n sourceAssetId?: string;\n /** Message ID of the source message (when quoting from chat). */\n sourceMessageId?: string;\n /** Role of the source message ('assistant' | 'user'). */\n sourceRole?: string;\n}\n\ninterface QuoteContextValue {\n quote: QuoteData | null;\n setQuote: (quote: QuoteData | null) => void;\n clearQuote: () => void;\n}\n\nconst QuoteCtx = createContext<QuoteContextValue | null>(null);\n\nexport function QuoteProvider({ children }: { children: ReactNode }) {\n const [quote, setQuote] = useState<QuoteData | null>(null);\n const clearQuote = useCallback(() => setQuote(null), []);\n\n const value = useMemo(() => ({ quote, setQuote, clearQuote }), [quote, setQuote, clearQuote]);\n\n return (\n <QuoteCtx.Provider value={value}>\n {children}\n </QuoteCtx.Provider>\n );\n}\n\nexport function useQuote(): QuoteContextValue {\n const ctx = useContext(QuoteCtx);\n if (!ctx) {\n throw new Error('[AthenaSDK] useQuote must be used within <QuoteProvider>');\n }\n return ctx;\n}\n\n/**\n * Listens for:\n * 1. Cmd+L / Ctrl+L keydown — grabs the current text selection and sets it as a quote.\n * `preventDefault` stops the browser from focusing the URL bar.\n * 2. PostMessage events from child iframes (asset panels, embedded content)\n * with type `athena-add-to-composer`.\n *\n * Mount this once inside AthenaProvider.\n */\nexport function useQuoteFromPostMessage() {\n const { setQuote } = useQuote();\n\n useEffect(() => {\n // ── Cmd+L / Ctrl+L keyboard handler ──────────────────────────────\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n const sel = window.getSelection();\n const selection = sel?.toString()?.trim();\n\n if (selection && selection.length > 0) {\n e.preventDefault();\n\n // Walk up from the selection anchor to find the message container.\n // assistant-ui message roots have data-role=\"assistant\" or data-role=\"user\".\n let messageId: string | undefined;\n let role: string | undefined;\n let node = sel?.anchorNode as HTMLElement | null;\n while (node && node !== document.body) {\n if (node instanceof HTMLElement) {\n const dataRole = node.getAttribute('data-role');\n if (dataRole === 'assistant' || dataRole === 'user') {\n role = dataRole;\n // assistant-ui sets data-message-id on message roots\n messageId = node.getAttribute('data-message-id') ?? undefined;\n break;\n }\n }\n node = node.parentElement;\n }\n\n setQuote({ text: selection, sourceRole: role, sourceMessageId: messageId });\n }\n }\n };\n\n // ── PostMessage handler (from iframes) ───────────────────────────\n // NOTE: Accepts messages from any origin by design. The SDK consumer\n // controls which iframes are embedded, defining the trust boundary.\n // A future `allowedOrigins` config could restrict this if needed.\n const handleMessage = (event: MessageEvent) => {\n const data = event.data;\n if (\n data &&\n typeof data === 'object' &&\n data.type === 'athena-add-to-composer' &&\n typeof data.text === 'string' &&\n data.text.trim().length > 0\n ) {\n setQuote({\n text: data.text,\n sourceTitle: data.sourceTitle,\n sourceAssetId: data.sourceAssetId,\n });\n }\n };\n\n document.addEventListener('keydown', handleKeydown, true); // capture phase to beat the browser\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('keydown', handleKeydown, true);\n window.removeEventListener('message', handleMessage);\n };\n }, [setQuote]);\n}\n"],"names":[],"mappings":";;AAqBA,MAAM,WAAW,cAAwC,IAAI;AAEtD,SAAS,cAAc,EAAE,YAAqC;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA2B,IAAI;AACzD,QAAM,aAAa,YAAY,MAAM,SAAS,IAAI,GAAG,CAAA,CAAE;AAEvD,QAAM,QAAQ,QAAQ,OAAO,EAAE,OAAO,UAAU,WAAA,IAAe,CAAC,OAAO,UAAU,UAAU,CAAC;AAE5F,SACE,oBAAC,SAAS,UAAT,EAAkB,OAChB,SAAA,CACH;AAEJ;AAEO,SAAS,WAA8B;AAC5C,QAAM,MAAM,WAAW,QAAQ;AAC/B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;AAWO,SAAS,0BAA0B;AACxC,QAAM,EAAE,SAAA,IAAa,SAAA;AAErB,YAAU,MAAM;AAEd,UAAM,gBAAgB,CAAC,MAAqB;;AAC1C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAA,MAAkB,KAAK;AAC3D,cAAM,MAAM,OAAO,aAAA;AACnB,cAAM,aAAY,gCAAK,eAAL,mBAAiB;AAEnC,YAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAE,eAAA;AAIF,cAAI;AACJ,cAAI;AACJ,cAAI,OAAO,2BAAK;AAChB,iBAAO,QAAQ,SAAS,SAAS,MAAM;AACrC,gBAAI,gBAAgB,aAAa;AAC/B,oBAAM,WAAW,KAAK,aAAa,WAAW;AAC9C,kBAAI,aAAa,eAAe,aAAa,QAAQ;AACnD,uBAAO;AAEP,4BAAY,KAAK,aAAa,iBAAiB,KAAK;AACpD;AAAA,cACF;AAAA,YACF;AACA,mBAAO,KAAK;AAAA,UACd;AAEA,mBAAS,EAAE,MAAM,WAAW,YAAY,MAAM,iBAAiB,WAAW;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAMA,UAAM,gBAAgB,CAAC,UAAwB;AAC7C,YAAM,OAAO,MAAM;AACnB,UACE,QACA,OAAO,SAAS,YAChB,KAAK,SAAS,4BACd,OAAO,KAAK,SAAS,YACrB,KAAK,KAAK,KAAA,EAAO,SAAS,GAC1B;AACA,iBAAS;AAAA,UACP,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,QAAA,CACrB;AAAA,MACH;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,iBAAiB,WAAW,aAAa;AAEhD,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAC3D,aAAO,oBAAoB,WAAW,aAAa;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACf;"}
1
+ {"version":3,"file":"QuoteContext.js","sources":["../../src/composer/QuoteContext.tsx"],"sourcesContent":["import { createContext, useCallback, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';\n\nexport interface QuoteData {\n /** The selected/quoted text. */\n text: string;\n /** Title of the source (e.g., asset title, document name). */\n sourceTitle?: string;\n /** Asset ID of the source (for attribution). */\n sourceAssetId?: string;\n /** Message ID of the source message (when quoting from chat). */\n sourceMessageId?: string;\n /** Role of the source message ('assistant' | 'user'). */\n sourceRole?: string;\n}\n\ninterface QuoteContextValue {\n quote: QuoteData | null;\n setQuote: (quote: QuoteData | null) => void;\n clearQuote: () => void;\n}\n\nconst QuoteCtx = createContext<QuoteContextValue | null>(null);\n\nexport function QuoteProvider({ children }: { children: ReactNode }) {\n const [quote, setQuote] = useState<QuoteData | null>(null);\n const clearQuote = useCallback(() => setQuote(null), []);\n\n const value = useMemo(() => ({ quote, setQuote, clearQuote }), [quote, setQuote, clearQuote]);\n\n return (\n <QuoteCtx.Provider value={value}>\n {children}\n </QuoteCtx.Provider>\n );\n}\n\nexport function useQuote(): QuoteContextValue {\n const ctx = useContext(QuoteCtx);\n if (!ctx) {\n throw new Error('[AthenaSDK] useQuote must be used within <QuoteProvider>');\n }\n return ctx;\n}\n\n/**\n * Whether a message came from a frame embedded directly in this window, for\n * any origin: the window of one of this document's iframes (an identity\n * comparison, safe cross-origin), or, for a frame inside a shadow root, a\n * window whose `parent` is this window (`parent` is readable on a\n * cross-origin window). A message port, a worker, this window itself, a\n * grandchild frame or an unrelated window is refused.\n */\nexport function isDirectChildFrame(source: MessageEventSource | null): boolean {\n if (!source || source === window) return false;\n for (const frame of document.querySelectorAll('iframe')) {\n if (frame.contentWindow === source) return true;\n }\n try {\n return Reflect.get(source, 'parent') === window;\n } catch {\n return false;\n }\n}\n\n/**\n * Listens for:\n * 1. Cmd+L / Ctrl+L keydown — grabs the current text selection and sets it as a quote.\n * `preventDefault` stops the browser from focusing the URL bar.\n * 2. PostMessage events from child iframes (asset panels, embedded content)\n * with type `athena-add-to-composer`, only from a frame embedded directly\n * in this window ({@link isDirectChildFrame}).\n *\n * Mount this once inside AthenaProvider.\n */\nexport function useQuoteFromPostMessage() {\n const { setQuote } = useQuote();\n\n useEffect(() => {\n // ── Cmd+L / Ctrl+L keyboard handler ──────────────────────────────\n const handleKeydown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'l') {\n const sel = window.getSelection();\n const selection = sel?.toString()?.trim();\n\n if (selection && selection.length > 0) {\n e.preventDefault();\n\n // Walk up from the selection anchor to find the message container.\n // assistant-ui message roots have data-role=\"assistant\" or data-role=\"user\".\n let messageId: string | undefined;\n let role: string | undefined;\n let node = sel?.anchorNode as HTMLElement | null;\n while (node && node !== document.body) {\n if (node instanceof HTMLElement) {\n const dataRole = node.getAttribute('data-role');\n if (dataRole === 'assistant' || dataRole === 'user') {\n role = dataRole;\n // assistant-ui sets data-message-id on message roots\n messageId = node.getAttribute('data-message-id') ?? undefined;\n break;\n }\n }\n node = node.parentElement;\n }\n\n setQuote({ text: selection, sourceRole: role, sourceMessageId: messageId });\n }\n }\n };\n\n // ── PostMessage handler (from iframes) ───────────────────────────\n // Accepts messages from any origin, but only from a frame embedded\n // directly in this window: the SDK consumer controls which iframes are\n // embedded, and that is the trust boundary. A popup, an opener or another\n // tab holding a reference to this window cannot stage composer text.\n const handleMessage = (event: MessageEvent) => {\n const data = event.data;\n if (\n data &&\n typeof data === 'object' &&\n data.type === 'athena-add-to-composer' &&\n typeof data.text === 'string' &&\n data.text.trim().length > 0 &&\n isDirectChildFrame(event.source)\n ) {\n setQuote({\n text: data.text,\n sourceTitle: data.sourceTitle,\n sourceAssetId: data.sourceAssetId,\n });\n }\n };\n\n document.addEventListener('keydown', handleKeydown, true); // capture phase to beat the browser\n window.addEventListener('message', handleMessage);\n\n return () => {\n document.removeEventListener('keydown', handleKeydown, true);\n window.removeEventListener('message', handleMessage);\n };\n }, [setQuote]);\n}\n"],"names":[],"mappings":";;AAqBA,MAAM,WAAW,cAAwC,IAAI;AAEtD,SAAS,cAAc,EAAE,YAAqC;AACnE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAA2B,IAAI;AACzD,QAAM,aAAa,YAAY,MAAM,SAAS,IAAI,GAAG,CAAA,CAAE;AAEvD,QAAM,QAAQ,QAAQ,OAAO,EAAE,OAAO,UAAU,WAAA,IAAe,CAAC,OAAO,UAAU,UAAU,CAAC;AAE5F,SACE,oBAAC,SAAS,UAAT,EAAkB,OAChB,SAAA,CACH;AAEJ;AAEO,SAAS,WAA8B;AAC5C,QAAM,MAAM,WAAW,QAAQ;AAC/B,MAAI,CAAC,KAAK;AACR,UAAM,IAAI,MAAM,0DAA0D;AAAA,EAC5E;AACA,SAAO;AACT;AAUO,SAAS,mBAAmB,QAA4C;AAC7E,MAAI,CAAC,UAAU,WAAW,OAAQ,QAAO;AACzC,aAAW,SAAS,SAAS,iBAAiB,QAAQ,GAAG;AACvD,QAAI,MAAM,kBAAkB,OAAQ,QAAO;AAAA,EAC7C;AACA,MAAI;AACF,WAAO,QAAQ,IAAI,QAAQ,QAAQ,MAAM;AAAA,EAC3C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAYO,SAAS,0BAA0B;AACxC,QAAM,EAAE,SAAA,IAAa,SAAA;AAErB,YAAU,MAAM;AAEd,UAAM,gBAAgB,CAAC,MAAqB;;AAC1C,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,IAAI,YAAA,MAAkB,KAAK;AAC3D,cAAM,MAAM,OAAO,aAAA;AACnB,cAAM,aAAY,gCAAK,eAAL,mBAAiB;AAEnC,YAAI,aAAa,UAAU,SAAS,GAAG;AACrC,YAAE,eAAA;AAIF,cAAI;AACJ,cAAI;AACJ,cAAI,OAAO,2BAAK;AAChB,iBAAO,QAAQ,SAAS,SAAS,MAAM;AACrC,gBAAI,gBAAgB,aAAa;AAC/B,oBAAM,WAAW,KAAK,aAAa,WAAW;AAC9C,kBAAI,aAAa,eAAe,aAAa,QAAQ;AACnD,uBAAO;AAEP,4BAAY,KAAK,aAAa,iBAAiB,KAAK;AACpD;AAAA,cACF;AAAA,YACF;AACA,mBAAO,KAAK;AAAA,UACd;AAEA,mBAAS,EAAE,MAAM,WAAW,YAAY,MAAM,iBAAiB,WAAW;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAOA,UAAM,gBAAgB,CAAC,UAAwB;AAC7C,YAAM,OAAO,MAAM;AACnB,UACE,QACA,OAAO,SAAS,YAChB,KAAK,SAAS,4BACd,OAAO,KAAK,SAAS,YACrB,KAAK,KAAK,OAAO,SAAS,KAC1B,mBAAmB,MAAM,MAAM,GAC/B;AACA,iBAAS;AAAA,UACP,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,eAAe,KAAK;AAAA,QAAA,CACrB;AAAA,MACH;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,eAAe,IAAI;AACxD,WAAO,iBAAiB,WAAW,aAAa;AAEhD,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,eAAe,IAAI;AAC3D,aAAO,oBAAoB,WAAW,aAAa;AAAA,IACrD;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AACf;"}
@@ -1 +1 @@
1
- {"version":3,"file":"athena-diagnostics.cjs","sources":["../../src/diagnostics/athena-diagnostics.ts"],"sourcesContent":["/**\n * SDK diagnostics: one structured event bus for everything the SDK does that\n * can be slow or can fail — auth acquisition, thread-list fetches, statewire\n * attach/snapshot/connection changes, sends, first tokens, and every error.\n *\n * Design:\n * - Module-level singleton (one SDK, one bus), configured by `AthenaProvider`\n * from its `debug` / `onDiagnostic` / `onError` props. Nothing here runs\n * until something calls `emit`, and with defaults it costs a ring-buffer push.\n * - Every event carries `t` (ms since page navigation start via\n * `performance.now()`) so the buffer reads as a waterfall. Timed spans use\n * `performance.mark` / `performance.measure` too, so the browser Performance\n * panel shows them as user timings without any extra tooling.\n * - Console output is opt-in (`debug` prop or `localStorage['athena:debug']`),\n * prefixed `[AthenaSDK]`, and level-gated. Errors always reach `onError`.\n * - PostHog forwarding (opt-in) sends the timing events as\n * `athena_sdk_<name>` with `duration_ms`, through the SDK's capture gate.\n *\n * Consumers read the buffer with `useAthenaDiagnostics()` or, in the browser\n * console, `window.__ATHENA_SDK__.diagnostics.snapshot()`.\n */\n\nimport { getPostHogInstance } from '../provider/PostHogProvider';\nimport { capturePosthogEvent, type PosthogCaptureClient } from '../lib/posthog/wrap-posthog-client';\nimport { ATHENA_REACT_SDK_VERSION } from '../version';\nimport { AthenaSdkError, isAthenaSdkError, toAthenaSdkError } from './errors';\n\nexport const ATHENA_DIAGNOSTIC_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];\n\nconst LEVEL_RANK: Record<AthenaDiagnosticLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Event names. Dotted `area.event` so a consumer can filter by prefix. Names\n * are part of the public contract: renaming one is a breaking change for\n * dashboards, so add new ones instead of repurposing.\n */\nexport type AthenaDiagnosticEventName =\n // Provider / configuration\n | 'sdk.init'\n | 'sdk.transport'\n | 'sdk.config.warning'\n // Auth acquisition + lifecycle\n | 'auth.bridge.start'\n | 'auth.bridge.ready'\n | 'auth.bridge.timeout'\n | 'auth.bridge.absent'\n | 'auth.bridge.error'\n | 'auth.bridge.retry'\n | 'auth.token.acquired'\n | 'auth.token.refreshed'\n | 'auth.token.refresh_failed'\n | 'auth.token.stale'\n | 'auth.sso.userinfo.start'\n | 'auth.sso.userinfo.done'\n | 'auth.sso.userinfo.error'\n // Thread list + history\n | 'threads.list.start'\n | 'threads.list.done'\n | 'threads.list.error'\n | 'threads.list.aborted'\n | 'threads.history.start'\n | 'threads.history.done'\n | 'threads.history.error'\n | 'threads.history.aborted'\n // Statewire transport\n | 'statewire.attach.start'\n | 'statewire.snapshot'\n | 'statewire.connection'\n | 'statewire.parked'\n | 'statewire.error'\n | 'statewire.read_only'\n // Runs\n | 'run.send'\n | 'run.first_token'\n | 'run.done'\n | 'run.error'\n // Composer\n | 'composer.attachment.error'\n | 'composer.cancel.error'\n | 'composer.dictation'\n | 'composer.handoff'\n // Tool UIs + frontend tools\n | 'tools.schema.warning'\n | 'tools.debug'\n // Mention suggestions\n | 'mentions.fetch.error'\n // Catch-all\n | 'sdk.error';\n\nexport interface AthenaDiagnosticEvent {\n /** Monotonic sequence number within this page. */\n seq: number;\n /** `performance.now()` at emit time (ms since navigation start). */\n t: number;\n /** Wall-clock ISO timestamp, for exports. */\n at: string;\n name: AthenaDiagnosticEventName;\n level: AthenaDiagnosticLevel;\n /** Milliseconds for a completed span (set on `*.done` / `*.ready` / `run.first_token`). */\n durationMs?: number;\n /** Structured context. Never contains credentials — see `redact`. */\n data?: Record<string, unknown>;\n /** Present on error-level events. */\n error?: AthenaSdkError;\n}\n\nexport interface AthenaDiagnosticsConfig {\n /** Minimum level echoed to the console. `false` disables console output. */\n console: AthenaDiagnosticLevel | false;\n /** Forward timing + error events to PostHog as `athena_sdk_*` events. */\n posthog: boolean;\n /** Receives every event (already redacted). */\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n /** Receives every error-level event's `AthenaSdkError`. */\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n /** Ring-buffer capacity. */\n bufferSize: number;\n}\n\nexport interface AthenaDiagnosticsSnapshot {\n sdkVersion: string;\n config: Omit<AthenaDiagnosticsConfig, 'onEvent' | 'onError'>;\n events: AthenaDiagnosticEvent[];\n /** Latest value per gauge (connection status, transport, auth mode, …). */\n gauges: Record<string, unknown>;\n /** Named span durations, most recent per name (`auth.bridge`, `threads.list`, …). */\n timings: Record<string, number>;\n}\n\nconst DEFAULT_CONFIG: AthenaDiagnosticsConfig = {\n console: false,\n posthog: false,\n bufferSize: 300,\n};\n\nconst CONSOLE_PREFIX = '[AthenaSDK]';\nconst LOCAL_STORAGE_DEBUG_KEY = 'athena:debug';\nconst GLOBAL_KEY = '__ATHENA_SDK__';\n\n// Keys whose values must never enter the buffer, the console, or PostHog.\nconst REDACT_KEYS = /token|secret|api[-_]?key|authorization|cookie|password/i;\n\nfunction redact(data: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (REDACT_KEYS.test(key)) {\n out[key] = typeof value === 'string' && value.length > 0 ? `<redacted:${value.length}>` : '<redacted>';\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction nowMs(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n}\n\nfunction readLocalDebugLevel(): AthenaDiagnosticLevel | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const raw = localStorage.getItem(LOCAL_STORAGE_DEBUG_KEY);\n if (!raw) return null;\n if ((ATHENA_DIAGNOSTIC_LEVELS as readonly string[]).includes(raw)) {\n return raw as AthenaDiagnosticLevel;\n }\n return raw === '1' || raw === 'true' ? 'debug' : null;\n } catch {\n return null;\n }\n}\n\ntype Listener = (event: AthenaDiagnosticEvent) => void;\n\nclass AthenaDiagnosticsBus {\n private config: AthenaDiagnosticsConfig = { ...DEFAULT_CONFIG };\n private events: AthenaDiagnosticEvent[] = [];\n private listeners = new Set<Listener>();\n private snapshotListeners = new Set<() => void>();\n private seq = 0;\n private gauges: Record<string, unknown> = {};\n private timings: Record<string, number> = {};\n private spans = new Map<string, number>();\n private cachedSnapshot: AthenaDiagnosticsSnapshot | null = null;\n\n configure(partial: Partial<AthenaDiagnosticsConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.events.length > this.config.bufferSize) {\n this.events = this.events.slice(-this.config.bufferSize);\n }\n this.invalidate();\n }\n\n getConfig(): AthenaDiagnosticsConfig {\n return this.config;\n }\n\n /** Effective console level: the configured one, or `localStorage['athena:debug']`. */\n private consoleLevel(): AthenaDiagnosticLevel | false {\n if (this.config.console !== false) return this.config.console;\n return readLocalDebugLevel() ?? false;\n }\n\n emit(\n name: AthenaDiagnosticEventName,\n options: {\n level?: AthenaDiagnosticLevel;\n data?: Record<string, unknown>;\n durationMs?: number;\n error?: unknown;\n } = {},\n ): AthenaDiagnosticEvent {\n const level = options.level ?? (options.error !== undefined ? 'error' : 'info');\n const error =\n options.error === undefined\n ? undefined\n : toAthenaSdkError(options.error, {\n code: 'unknown',\n message: `Unexpected error during ${name}`,\n });\n const event: AthenaDiagnosticEvent = {\n seq: ++this.seq,\n t: Math.round(nowMs() * 10) / 10,\n at: new Date().toISOString(),\n name,\n level,\n ...(options.durationMs !== undefined && { durationMs: Math.round(options.durationMs) }),\n ...(options.data && { data: redact(options.data) }),\n ...(error && { error }),\n };\n\n this.events.push(event);\n if (this.events.length > this.config.bufferSize) {\n this.events.splice(0, this.events.length - this.config.bufferSize);\n }\n if (event.durationMs !== undefined) {\n this.timings[name] = event.durationMs;\n }\n this.invalidate();\n\n this.echoToConsole(event);\n this.forwardToPostHog(event);\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (listenerError) {\n console.error(`${CONSOLE_PREFIX} diagnostics listener threw`, listenerError);\n }\n }\n if (this.config.onEvent) {\n try {\n this.config.onEvent(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic callback threw`, callbackError);\n }\n }\n if (error && this.config.onError) {\n try {\n this.config.onError(error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError callback threw`, callbackError);\n }\n }\n for (const notify of this.snapshotListeners) notify();\n return event;\n }\n\n /** Record an error-level event. Returns the normalized `AthenaSdkError`. */\n error(\n name: AthenaDiagnosticEventName,\n error: unknown,\n fallback: { code: AthenaSdkError['code']; message: string; hint?: string },\n data?: Record<string, unknown>,\n ): AthenaSdkError {\n const normalized = toAthenaSdkError(error, fallback);\n this.emit(name, { level: 'error', error: normalized, data });\n return normalized;\n }\n\n /**\n * Start a named span. Returns a function that ends it and emits `doneName`\n * with `durationMs` (or `errorName` when passed an error). Also records a\n * `performance.measure` so the span shows in the browser's Performance panel.\n */\n span(\n spanName: string,\n startName: AthenaDiagnosticEventName,\n data?: Record<string, unknown>,\n ): (\n doneName: AthenaDiagnosticEventName,\n doneOptions?: { data?: Record<string, unknown>; error?: unknown },\n ) => number {\n const startedAt = nowMs();\n this.spans.set(spanName, startedAt);\n this.mark(`${spanName}:start`);\n this.emit(startName, { level: 'debug', data });\n return (doneName, doneOptions = {}) => {\n const durationMs = nowMs() - startedAt;\n this.spans.delete(spanName);\n this.mark(`${spanName}:end`);\n this.measure(spanName);\n this.emit(doneName, {\n level: doneOptions.error !== undefined ? 'error' : 'info',\n durationMs,\n data: doneOptions.data,\n error: doneOptions.error,\n });\n return durationMs;\n };\n }\n\n /** Set a gauge (latest-value metric): connection status, transport, auth mode. */\n gauge(name: string, value: unknown): void {\n if (this.gauges[name] === value) return;\n this.gauges[name] = value;\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Additive consumer registration: every registered consumer receives every\n * event (and every error), unlike the single `onEvent`/`onError` config\n * slots which are last-writer-wins. `AthenaProvider` registers through this\n * so two mounted providers never clobber each other's callbacks.\n */\n addConsumer(consumer: {\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n }): () => void {\n return this.subscribe((event) => {\n try {\n consumer.onEvent?.(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic consumer threw`, callbackError);\n }\n if (event.error) {\n try {\n consumer.onError?.(event.error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError consumer threw`, callbackError);\n }\n }\n });\n }\n\n /** Subscribe to snapshot changes (for `useSyncExternalStore`). */\n subscribeSnapshot(listener: () => void): () => void {\n this.snapshotListeners.add(listener);\n return () => {\n this.snapshotListeners.delete(listener);\n };\n }\n\n snapshot(): AthenaDiagnosticsSnapshot {\n if (!this.cachedSnapshot) {\n const { onEvent: _onEvent, onError: _onError, ...config } = this.config;\n this.cachedSnapshot = {\n sdkVersion: ATHENA_REACT_SDK_VERSION,\n config,\n events: [...this.events],\n gauges: { ...this.gauges },\n timings: { ...this.timings },\n };\n }\n return this.cachedSnapshot;\n }\n\n /** Copy-paste-able JSON of the current snapshot (errors serialized). */\n export(): string {\n return JSON.stringify(\n this.snapshot(),\n (_key, value) => (isAthenaSdkError(value) ? value.toJSON() : value),\n 2,\n );\n }\n\n clear(): void {\n this.events = [];\n this.timings = {};\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n private invalidate(): void {\n this.cachedSnapshot = null;\n }\n\n private mark(name: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {\n performance.mark(`athena-sdk:${name}`);\n }\n } catch {\n // User Timing is best-effort.\n }\n }\n\n private measure(spanName: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.measure === 'function') {\n performance.measure(\n `athena-sdk:${spanName}`,\n `athena-sdk:${spanName}:start`,\n `athena-sdk:${spanName}:end`,\n );\n }\n } catch {\n // A missing start mark (e.g. after `performance.clearMarks`) is not an error.\n }\n }\n\n private echoToConsole(event: AthenaDiagnosticEvent): void {\n const level = this.consoleLevel();\n if (level === false) {\n // Errors are never silent: with console output off they still log once,\n // compactly, so a broken integration is visible without enabling debug.\n if (event.level === 'error' && event.error) {\n console.error(`${CONSOLE_PREFIX} ${event.name}: ${event.error.describe()}`, event.data ?? '');\n }\n return;\n }\n if (LEVEL_RANK[event.level] < LEVEL_RANK[level]) return;\n const suffix = event.durationMs !== undefined ? ` (${event.durationMs}ms)` : '';\n const line = `${CONSOLE_PREFIX} ${event.name}${suffix}`;\n const payload = event.error ? { ...event.data, error: event.error.describe() } : event.data;\n const method =\n event.level === 'error'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : event.level === 'debug'\n ? console.debug\n : console.info;\n method(line, payload ?? '');\n }\n\n private forwardToPostHog(event: AthenaDiagnosticEvent): void {\n if (!this.config.posthog) return;\n if (event.durationMs === undefined && event.level !== 'error') return;\n try {\n const client = getPostHogInstance<PosthogCaptureClient>();\n capturePosthogEvent(client, `athena_sdk_${event.name.replace(/\\./g, '_')}`, {\n sdk_version: ATHENA_REACT_SDK_VERSION,\n level: event.level,\n ...(event.durationMs !== undefined && { duration_ms: event.durationMs }),\n ...(event.error && {\n error_code: event.error.code,\n error_message: event.error.message,\n error_status: event.error.status,\n }),\n ...event.data,\n });\n } catch {\n // Analytics must never break the chat.\n }\n }\n}\n\nexport const athenaDiagnostics = new AthenaDiagnosticsBus();\n\n/**\n * Expose the bus on `window.__ATHENA_SDK__` so an integrator can inspect the\n * waterfall from the console (`__ATHENA_SDK__.diagnostics.snapshot()`) or turn\n * console echo on without a rebuild (`localStorage.setItem('athena:debug','debug')`).\n */\nexport function installAthenaDiagnosticsGlobal(): void {\n if (typeof window === 'undefined') return;\n const globalWindow = window as unknown as Record<string, unknown>;\n const existing = (globalWindow[GLOBAL_KEY] ?? {}) as Record<string, unknown>;\n globalWindow[GLOBAL_KEY] = {\n ...existing,\n version: ATHENA_REACT_SDK_VERSION,\n diagnostics: {\n snapshot: () => athenaDiagnostics.snapshot(),\n export: () => athenaDiagnostics.export(),\n clear: () => athenaDiagnostics.clear(),\n enableConsole: (level: AthenaDiagnosticLevel = 'debug') => {\n try {\n localStorage.setItem(LOCAL_STORAGE_DEBUG_KEY, level);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: level });\n },\n disableConsole: () => {\n try {\n localStorage.removeItem(LOCAL_STORAGE_DEBUG_KEY);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: false });\n },\n },\n };\n}\n\n/** Normalize the provider's `debug` prop into a diagnostics config patch. */\nexport function debugPropToDiagnosticsConfig(\n debug: boolean | AthenaDiagnosticLevel | Partial<AthenaDiagnosticsConfig> | undefined,\n): Partial<AthenaDiagnosticsConfig> {\n if (debug === undefined) return {};\n if (debug === true) return { console: 'debug' };\n if (debug === false) return { console: false };\n if (typeof debug === 'string') return { console: debug };\n return debug;\n}\n"],"names":["toAthenaSdkError","ATHENA_REACT_SDK_VERSION","isAthenaSdkError","getPostHogInstance","capturePosthogEvent"],"mappings":"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"}
1
+ {"version":3,"file":"athena-diagnostics.cjs","sources":["../../src/diagnostics/athena-diagnostics.ts"],"sourcesContent":["/**\n * SDK diagnostics: one structured event bus for everything the SDK does that\n * can be slow or can fail — auth acquisition, thread-list fetches, statewire\n * attach/snapshot/connection changes, sends, first tokens, and every error.\n *\n * Design:\n * - Module-level singleton (one SDK, one bus), configured by `AthenaProvider`\n * from its `debug` / `onDiagnostic` / `onError` props. Nothing here runs\n * until something calls `emit`, and with defaults it costs a ring-buffer push.\n * - Every event carries `t` (ms since page navigation start via\n * `performance.now()`) so the buffer reads as a waterfall. Timed spans use\n * `performance.mark` / `performance.measure` too, so the browser Performance\n * panel shows them as user timings without any extra tooling.\n * - Console output is opt-in (`debug` prop or `localStorage['athena:debug']`),\n * prefixed `[AthenaSDK]`, and level-gated. Errors always reach `onError`.\n * - PostHog forwarding (opt-in) sends the timing events as\n * `athena_sdk_<name>` with `duration_ms`, through the SDK's capture gate.\n *\n * Consumers read the buffer with `useAthenaDiagnostics()` or, in the browser\n * console, `window.__ATHENA_SDK__.diagnostics.snapshot()`.\n */\n\nimport { getPostHogInstance } from '../provider/PostHogProvider';\nimport { capturePosthogEvent, type PosthogCaptureClient } from '../lib/posthog/wrap-posthog-client';\nimport { ATHENA_REACT_SDK_VERSION } from '../version';\nimport { AthenaSdkError, isAthenaSdkError, toAthenaSdkError } from './errors';\n\nexport const ATHENA_DIAGNOSTIC_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];\n\nconst LEVEL_RANK: Record<AthenaDiagnosticLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Event names. Dotted `area.event` so a consumer can filter by prefix. Names\n * are part of the public contract: renaming one is a breaking change for\n * dashboards, so add new ones instead of repurposing.\n */\nexport type AthenaDiagnosticEventName =\n // Provider / configuration\n | 'sdk.init'\n | 'sdk.transport'\n | 'sdk.config.warning'\n // Auth acquisition + lifecycle\n | 'auth.bridge.start'\n | 'auth.bridge.ready'\n | 'auth.bridge.timeout'\n | 'auth.bridge.absent'\n | 'auth.bridge.error'\n | 'auth.bridge.retry'\n | 'auth.bridge.config_rejected'\n | 'auth.token.acquired'\n | 'auth.token.refreshed'\n | 'auth.token.refresh_failed'\n | 'auth.token.stale'\n | 'auth.sso.userinfo.start'\n | 'auth.sso.userinfo.done'\n | 'auth.sso.userinfo.error'\n // Thread list + history\n | 'threads.list.start'\n | 'threads.list.done'\n | 'threads.list.error'\n | 'threads.list.aborted'\n | 'threads.history.start'\n | 'threads.history.done'\n | 'threads.history.error'\n | 'threads.history.aborted'\n // Statewire transport\n | 'statewire.attach.start'\n | 'statewire.snapshot'\n | 'statewire.connection'\n | 'statewire.parked'\n | 'statewire.error'\n | 'statewire.read_only'\n // Runs\n | 'run.send'\n | 'run.first_token'\n | 'run.done'\n | 'run.error'\n // Composer\n | 'composer.attachment.error'\n | 'composer.cancel.error'\n | 'composer.dictation'\n | 'composer.handoff'\n // Tool UIs + frontend tools\n | 'tools.schema.warning'\n | 'tools.debug'\n // Mention suggestions\n | 'mentions.fetch.error'\n // Catch-all\n | 'sdk.error';\n\nexport interface AthenaDiagnosticEvent {\n /** Monotonic sequence number within this page. */\n seq: number;\n /** `performance.now()` at emit time (ms since navigation start). */\n t: number;\n /** Wall-clock ISO timestamp, for exports. */\n at: string;\n name: AthenaDiagnosticEventName;\n level: AthenaDiagnosticLevel;\n /** Milliseconds for a completed span (set on `*.done` / `*.ready` / `run.first_token`). */\n durationMs?: number;\n /** Structured context. Never contains credentials — see `redact`. */\n data?: Record<string, unknown>;\n /** Present on error-level events. */\n error?: AthenaSdkError;\n}\n\nexport interface AthenaDiagnosticsConfig {\n /** Minimum level echoed to the console. `false` disables console output. */\n console: AthenaDiagnosticLevel | false;\n /** Forward timing + error events to PostHog as `athena_sdk_*` events. */\n posthog: boolean;\n /** Receives every event (already redacted). */\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n /** Receives every error-level event's `AthenaSdkError`. */\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n /** Ring-buffer capacity. */\n bufferSize: number;\n}\n\nexport interface AthenaDiagnosticsSnapshot {\n sdkVersion: string;\n config: Omit<AthenaDiagnosticsConfig, 'onEvent' | 'onError'>;\n events: AthenaDiagnosticEvent[];\n /** Latest value per gauge (connection status, transport, auth mode, …). */\n gauges: Record<string, unknown>;\n /** Named span durations, most recent per name (`auth.bridge`, `threads.list`, …). */\n timings: Record<string, number>;\n}\n\nconst DEFAULT_CONFIG: AthenaDiagnosticsConfig = {\n console: false,\n posthog: false,\n bufferSize: 300,\n};\n\nconst CONSOLE_PREFIX = '[AthenaSDK]';\nconst LOCAL_STORAGE_DEBUG_KEY = 'athena:debug';\nconst GLOBAL_KEY = '__ATHENA_SDK__';\n\n// Keys whose values must never enter the buffer, the console, or PostHog.\nconst REDACT_KEYS = /token|secret|api[-_]?key|authorization|cookie|password/i;\n\nfunction redact(data: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (REDACT_KEYS.test(key)) {\n out[key] = typeof value === 'string' && value.length > 0 ? `<redacted:${value.length}>` : '<redacted>';\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction nowMs(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n}\n\nfunction readLocalDebugLevel(): AthenaDiagnosticLevel | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const raw = localStorage.getItem(LOCAL_STORAGE_DEBUG_KEY);\n if (!raw) return null;\n if ((ATHENA_DIAGNOSTIC_LEVELS as readonly string[]).includes(raw)) {\n return raw as AthenaDiagnosticLevel;\n }\n return raw === '1' || raw === 'true' ? 'debug' : null;\n } catch {\n return null;\n }\n}\n\ntype Listener = (event: AthenaDiagnosticEvent) => void;\n\nclass AthenaDiagnosticsBus {\n private config: AthenaDiagnosticsConfig = { ...DEFAULT_CONFIG };\n private events: AthenaDiagnosticEvent[] = [];\n private listeners = new Set<Listener>();\n private snapshotListeners = new Set<() => void>();\n private seq = 0;\n private gauges: Record<string, unknown> = {};\n private timings: Record<string, number> = {};\n private spans = new Map<string, number>();\n private cachedSnapshot: AthenaDiagnosticsSnapshot | null = null;\n\n configure(partial: Partial<AthenaDiagnosticsConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.events.length > this.config.bufferSize) {\n this.events = this.events.slice(-this.config.bufferSize);\n }\n this.invalidate();\n }\n\n getConfig(): AthenaDiagnosticsConfig {\n return this.config;\n }\n\n /** Effective console level: the configured one, or `localStorage['athena:debug']`. */\n private consoleLevel(): AthenaDiagnosticLevel | false {\n if (this.config.console !== false) return this.config.console;\n return readLocalDebugLevel() ?? false;\n }\n\n emit(\n name: AthenaDiagnosticEventName,\n options: {\n level?: AthenaDiagnosticLevel;\n data?: Record<string, unknown>;\n durationMs?: number;\n error?: unknown;\n } = {},\n ): AthenaDiagnosticEvent {\n const level = options.level ?? (options.error !== undefined ? 'error' : 'info');\n const error =\n options.error === undefined\n ? undefined\n : toAthenaSdkError(options.error, {\n code: 'unknown',\n message: `Unexpected error during ${name}`,\n });\n const event: AthenaDiagnosticEvent = {\n seq: ++this.seq,\n t: Math.round(nowMs() * 10) / 10,\n at: new Date().toISOString(),\n name,\n level,\n ...(options.durationMs !== undefined && { durationMs: Math.round(options.durationMs) }),\n ...(options.data && { data: redact(options.data) }),\n ...(error && { error }),\n };\n\n this.events.push(event);\n if (this.events.length > this.config.bufferSize) {\n this.events.splice(0, this.events.length - this.config.bufferSize);\n }\n if (event.durationMs !== undefined) {\n this.timings[name] = event.durationMs;\n }\n this.invalidate();\n\n this.echoToConsole(event);\n this.forwardToPostHog(event);\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (listenerError) {\n console.error(`${CONSOLE_PREFIX} diagnostics listener threw`, listenerError);\n }\n }\n if (this.config.onEvent) {\n try {\n this.config.onEvent(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic callback threw`, callbackError);\n }\n }\n if (error && this.config.onError) {\n try {\n this.config.onError(error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError callback threw`, callbackError);\n }\n }\n for (const notify of this.snapshotListeners) notify();\n return event;\n }\n\n /** Record an error-level event. Returns the normalized `AthenaSdkError`. */\n error(\n name: AthenaDiagnosticEventName,\n error: unknown,\n fallback: { code: AthenaSdkError['code']; message: string; hint?: string },\n data?: Record<string, unknown>,\n ): AthenaSdkError {\n const normalized = toAthenaSdkError(error, fallback);\n this.emit(name, { level: 'error', error: normalized, data });\n return normalized;\n }\n\n /**\n * Start a named span. Returns a function that ends it and emits `doneName`\n * with `durationMs` (or `errorName` when passed an error). Also records a\n * `performance.measure` so the span shows in the browser's Performance panel.\n */\n span(\n spanName: string,\n startName: AthenaDiagnosticEventName,\n data?: Record<string, unknown>,\n ): (\n doneName: AthenaDiagnosticEventName,\n doneOptions?: { data?: Record<string, unknown>; error?: unknown },\n ) => number {\n const startedAt = nowMs();\n this.spans.set(spanName, startedAt);\n this.mark(`${spanName}:start`);\n this.emit(startName, { level: 'debug', data });\n return (doneName, doneOptions = {}) => {\n const durationMs = nowMs() - startedAt;\n this.spans.delete(spanName);\n this.mark(`${spanName}:end`);\n this.measure(spanName);\n this.emit(doneName, {\n level: doneOptions.error !== undefined ? 'error' : 'info',\n durationMs,\n data: doneOptions.data,\n error: doneOptions.error,\n });\n return durationMs;\n };\n }\n\n /** Set a gauge (latest-value metric): connection status, transport, auth mode. */\n gauge(name: string, value: unknown): void {\n if (this.gauges[name] === value) return;\n this.gauges[name] = value;\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Additive consumer registration: every registered consumer receives every\n * event (and every error), unlike the single `onEvent`/`onError` config\n * slots which are last-writer-wins. `AthenaProvider` registers through this\n * so two mounted providers never clobber each other's callbacks.\n */\n addConsumer(consumer: {\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n }): () => void {\n return this.subscribe((event) => {\n try {\n consumer.onEvent?.(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic consumer threw`, callbackError);\n }\n if (event.error) {\n try {\n consumer.onError?.(event.error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError consumer threw`, callbackError);\n }\n }\n });\n }\n\n /** Subscribe to snapshot changes (for `useSyncExternalStore`). */\n subscribeSnapshot(listener: () => void): () => void {\n this.snapshotListeners.add(listener);\n return () => {\n this.snapshotListeners.delete(listener);\n };\n }\n\n snapshot(): AthenaDiagnosticsSnapshot {\n if (!this.cachedSnapshot) {\n const { onEvent: _onEvent, onError: _onError, ...config } = this.config;\n this.cachedSnapshot = {\n sdkVersion: ATHENA_REACT_SDK_VERSION,\n config,\n events: [...this.events],\n gauges: { ...this.gauges },\n timings: { ...this.timings },\n };\n }\n return this.cachedSnapshot;\n }\n\n /** Copy-paste-able JSON of the current snapshot (errors serialized). */\n export(): string {\n return JSON.stringify(\n this.snapshot(),\n (_key, value) => (isAthenaSdkError(value) ? value.toJSON() : value),\n 2,\n );\n }\n\n clear(): void {\n this.events = [];\n this.timings = {};\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n private invalidate(): void {\n this.cachedSnapshot = null;\n }\n\n private mark(name: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {\n performance.mark(`athena-sdk:${name}`);\n }\n } catch {\n // User Timing is best-effort.\n }\n }\n\n private measure(spanName: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.measure === 'function') {\n performance.measure(\n `athena-sdk:${spanName}`,\n `athena-sdk:${spanName}:start`,\n `athena-sdk:${spanName}:end`,\n );\n }\n } catch {\n // A missing start mark (e.g. after `performance.clearMarks`) is not an error.\n }\n }\n\n private echoToConsole(event: AthenaDiagnosticEvent): void {\n const level = this.consoleLevel();\n if (level === false) {\n // Errors are never silent: with console output off they still log once,\n // compactly, so a broken integration is visible without enabling debug.\n if (event.level === 'error' && event.error) {\n console.error(`${CONSOLE_PREFIX} ${event.name}: ${event.error.describe()}`, event.data ?? '');\n }\n return;\n }\n if (LEVEL_RANK[event.level] < LEVEL_RANK[level]) return;\n const suffix = event.durationMs !== undefined ? ` (${event.durationMs}ms)` : '';\n const line = `${CONSOLE_PREFIX} ${event.name}${suffix}`;\n const payload = event.error ? { ...event.data, error: event.error.describe() } : event.data;\n const method =\n event.level === 'error'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : event.level === 'debug'\n ? console.debug\n : console.info;\n method(line, payload ?? '');\n }\n\n private forwardToPostHog(event: AthenaDiagnosticEvent): void {\n if (!this.config.posthog) return;\n if (event.durationMs === undefined && event.level !== 'error') return;\n try {\n const client = getPostHogInstance<PosthogCaptureClient>();\n capturePosthogEvent(client, `athena_sdk_${event.name.replace(/\\./g, '_')}`, {\n sdk_version: ATHENA_REACT_SDK_VERSION,\n level: event.level,\n ...(event.durationMs !== undefined && { duration_ms: event.durationMs }),\n ...(event.error && {\n error_code: event.error.code,\n error_message: event.error.message,\n error_status: event.error.status,\n }),\n ...event.data,\n });\n } catch {\n // Analytics must never break the chat.\n }\n }\n}\n\nexport const athenaDiagnostics = new AthenaDiagnosticsBus();\n\n/**\n * Expose the bus on `window.__ATHENA_SDK__` so an integrator can inspect the\n * waterfall from the console (`__ATHENA_SDK__.diagnostics.snapshot()`) or turn\n * console echo on without a rebuild (`localStorage.setItem('athena:debug','debug')`).\n */\nexport function installAthenaDiagnosticsGlobal(): void {\n if (typeof window === 'undefined') return;\n const globalWindow = window as unknown as Record<string, unknown>;\n const existing = (globalWindow[GLOBAL_KEY] ?? {}) as Record<string, unknown>;\n globalWindow[GLOBAL_KEY] = {\n ...existing,\n version: ATHENA_REACT_SDK_VERSION,\n diagnostics: {\n snapshot: () => athenaDiagnostics.snapshot(),\n export: () => athenaDiagnostics.export(),\n clear: () => athenaDiagnostics.clear(),\n enableConsole: (level: AthenaDiagnosticLevel = 'debug') => {\n try {\n localStorage.setItem(LOCAL_STORAGE_DEBUG_KEY, level);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: level });\n },\n disableConsole: () => {\n try {\n localStorage.removeItem(LOCAL_STORAGE_DEBUG_KEY);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: false });\n },\n },\n };\n}\n\n/** Normalize the provider's `debug` prop into a diagnostics config patch. */\nexport function debugPropToDiagnosticsConfig(\n debug: boolean | AthenaDiagnosticLevel | Partial<AthenaDiagnosticsConfig> | undefined,\n): Partial<AthenaDiagnosticsConfig> {\n if (debug === undefined) return {};\n if (debug === true) return { console: 'debug' };\n if (debug === false) return { console: false };\n if (typeof debug === 'string') return { console: debug };\n return debug;\n}\n"],"names":["toAthenaSdkError","ATHENA_REACT_SDK_VERSION","isAthenaSdkError","getPostHogInstance","capturePosthogEvent"],"mappings":"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"}
@@ -27,7 +27,7 @@ export type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];
27
27
  * are part of the public contract: renaming one is a breaking change for
28
28
  * dashboards, so add new ones instead of repurposing.
29
29
  */
30
- export type AthenaDiagnosticEventName = 'sdk.init' | 'sdk.transport' | 'sdk.config.warning' | 'auth.bridge.start' | 'auth.bridge.ready' | 'auth.bridge.timeout' | 'auth.bridge.absent' | 'auth.bridge.error' | 'auth.bridge.retry' | 'auth.token.acquired' | 'auth.token.refreshed' | 'auth.token.refresh_failed' | 'auth.token.stale' | 'auth.sso.userinfo.start' | 'auth.sso.userinfo.done' | 'auth.sso.userinfo.error' | 'threads.list.start' | 'threads.list.done' | 'threads.list.error' | 'threads.list.aborted' | 'threads.history.start' | 'threads.history.done' | 'threads.history.error' | 'threads.history.aborted' | 'statewire.attach.start' | 'statewire.snapshot' | 'statewire.connection' | 'statewire.parked' | 'statewire.error' | 'statewire.read_only' | 'run.send' | 'run.first_token' | 'run.done' | 'run.error' | 'composer.attachment.error' | 'composer.cancel.error' | 'composer.dictation' | 'composer.handoff' | 'tools.schema.warning' | 'tools.debug' | 'mentions.fetch.error' | 'sdk.error';
30
+ export type AthenaDiagnosticEventName = 'sdk.init' | 'sdk.transport' | 'sdk.config.warning' | 'auth.bridge.start' | 'auth.bridge.ready' | 'auth.bridge.timeout' | 'auth.bridge.absent' | 'auth.bridge.error' | 'auth.bridge.retry' | 'auth.bridge.config_rejected' | 'auth.token.acquired' | 'auth.token.refreshed' | 'auth.token.refresh_failed' | 'auth.token.stale' | 'auth.sso.userinfo.start' | 'auth.sso.userinfo.done' | 'auth.sso.userinfo.error' | 'threads.list.start' | 'threads.list.done' | 'threads.list.error' | 'threads.list.aborted' | 'threads.history.start' | 'threads.history.done' | 'threads.history.error' | 'threads.history.aborted' | 'statewire.attach.start' | 'statewire.snapshot' | 'statewire.connection' | 'statewire.parked' | 'statewire.error' | 'statewire.read_only' | 'run.send' | 'run.first_token' | 'run.done' | 'run.error' | 'composer.attachment.error' | 'composer.cancel.error' | 'composer.dictation' | 'composer.handoff' | 'tools.schema.warning' | 'tools.debug' | 'mentions.fetch.error' | 'sdk.error';
31
31
  export interface AthenaDiagnosticEvent {
32
32
  /** Monotonic sequence number within this page. */
33
33
  seq: number;
@@ -1 +1 @@
1
- {"version":3,"file":"athena-diagnostics.js","sources":["../../src/diagnostics/athena-diagnostics.ts"],"sourcesContent":["/**\n * SDK diagnostics: one structured event bus for everything the SDK does that\n * can be slow or can fail — auth acquisition, thread-list fetches, statewire\n * attach/snapshot/connection changes, sends, first tokens, and every error.\n *\n * Design:\n * - Module-level singleton (one SDK, one bus), configured by `AthenaProvider`\n * from its `debug` / `onDiagnostic` / `onError` props. Nothing here runs\n * until something calls `emit`, and with defaults it costs a ring-buffer push.\n * - Every event carries `t` (ms since page navigation start via\n * `performance.now()`) so the buffer reads as a waterfall. Timed spans use\n * `performance.mark` / `performance.measure` too, so the browser Performance\n * panel shows them as user timings without any extra tooling.\n * - Console output is opt-in (`debug` prop or `localStorage['athena:debug']`),\n * prefixed `[AthenaSDK]`, and level-gated. Errors always reach `onError`.\n * - PostHog forwarding (opt-in) sends the timing events as\n * `athena_sdk_<name>` with `duration_ms`, through the SDK's capture gate.\n *\n * Consumers read the buffer with `useAthenaDiagnostics()` or, in the browser\n * console, `window.__ATHENA_SDK__.diagnostics.snapshot()`.\n */\n\nimport { getPostHogInstance } from '../provider/PostHogProvider';\nimport { capturePosthogEvent, type PosthogCaptureClient } from '../lib/posthog/wrap-posthog-client';\nimport { ATHENA_REACT_SDK_VERSION } from '../version';\nimport { AthenaSdkError, isAthenaSdkError, toAthenaSdkError } from './errors';\n\nexport const ATHENA_DIAGNOSTIC_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];\n\nconst LEVEL_RANK: Record<AthenaDiagnosticLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Event names. Dotted `area.event` so a consumer can filter by prefix. Names\n * are part of the public contract: renaming one is a breaking change for\n * dashboards, so add new ones instead of repurposing.\n */\nexport type AthenaDiagnosticEventName =\n // Provider / configuration\n | 'sdk.init'\n | 'sdk.transport'\n | 'sdk.config.warning'\n // Auth acquisition + lifecycle\n | 'auth.bridge.start'\n | 'auth.bridge.ready'\n | 'auth.bridge.timeout'\n | 'auth.bridge.absent'\n | 'auth.bridge.error'\n | 'auth.bridge.retry'\n | 'auth.token.acquired'\n | 'auth.token.refreshed'\n | 'auth.token.refresh_failed'\n | 'auth.token.stale'\n | 'auth.sso.userinfo.start'\n | 'auth.sso.userinfo.done'\n | 'auth.sso.userinfo.error'\n // Thread list + history\n | 'threads.list.start'\n | 'threads.list.done'\n | 'threads.list.error'\n | 'threads.list.aborted'\n | 'threads.history.start'\n | 'threads.history.done'\n | 'threads.history.error'\n | 'threads.history.aborted'\n // Statewire transport\n | 'statewire.attach.start'\n | 'statewire.snapshot'\n | 'statewire.connection'\n | 'statewire.parked'\n | 'statewire.error'\n | 'statewire.read_only'\n // Runs\n | 'run.send'\n | 'run.first_token'\n | 'run.done'\n | 'run.error'\n // Composer\n | 'composer.attachment.error'\n | 'composer.cancel.error'\n | 'composer.dictation'\n | 'composer.handoff'\n // Tool UIs + frontend tools\n | 'tools.schema.warning'\n | 'tools.debug'\n // Mention suggestions\n | 'mentions.fetch.error'\n // Catch-all\n | 'sdk.error';\n\nexport interface AthenaDiagnosticEvent {\n /** Monotonic sequence number within this page. */\n seq: number;\n /** `performance.now()` at emit time (ms since navigation start). */\n t: number;\n /** Wall-clock ISO timestamp, for exports. */\n at: string;\n name: AthenaDiagnosticEventName;\n level: AthenaDiagnosticLevel;\n /** Milliseconds for a completed span (set on `*.done` / `*.ready` / `run.first_token`). */\n durationMs?: number;\n /** Structured context. Never contains credentials — see `redact`. */\n data?: Record<string, unknown>;\n /** Present on error-level events. */\n error?: AthenaSdkError;\n}\n\nexport interface AthenaDiagnosticsConfig {\n /** Minimum level echoed to the console. `false` disables console output. */\n console: AthenaDiagnosticLevel | false;\n /** Forward timing + error events to PostHog as `athena_sdk_*` events. */\n posthog: boolean;\n /** Receives every event (already redacted). */\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n /** Receives every error-level event's `AthenaSdkError`. */\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n /** Ring-buffer capacity. */\n bufferSize: number;\n}\n\nexport interface AthenaDiagnosticsSnapshot {\n sdkVersion: string;\n config: Omit<AthenaDiagnosticsConfig, 'onEvent' | 'onError'>;\n events: AthenaDiagnosticEvent[];\n /** Latest value per gauge (connection status, transport, auth mode, …). */\n gauges: Record<string, unknown>;\n /** Named span durations, most recent per name (`auth.bridge`, `threads.list`, …). */\n timings: Record<string, number>;\n}\n\nconst DEFAULT_CONFIG: AthenaDiagnosticsConfig = {\n console: false,\n posthog: false,\n bufferSize: 300,\n};\n\nconst CONSOLE_PREFIX = '[AthenaSDK]';\nconst LOCAL_STORAGE_DEBUG_KEY = 'athena:debug';\nconst GLOBAL_KEY = '__ATHENA_SDK__';\n\n// Keys whose values must never enter the buffer, the console, or PostHog.\nconst REDACT_KEYS = /token|secret|api[-_]?key|authorization|cookie|password/i;\n\nfunction redact(data: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (REDACT_KEYS.test(key)) {\n out[key] = typeof value === 'string' && value.length > 0 ? `<redacted:${value.length}>` : '<redacted>';\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction nowMs(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n}\n\nfunction readLocalDebugLevel(): AthenaDiagnosticLevel | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const raw = localStorage.getItem(LOCAL_STORAGE_DEBUG_KEY);\n if (!raw) return null;\n if ((ATHENA_DIAGNOSTIC_LEVELS as readonly string[]).includes(raw)) {\n return raw as AthenaDiagnosticLevel;\n }\n return raw === '1' || raw === 'true' ? 'debug' : null;\n } catch {\n return null;\n }\n}\n\ntype Listener = (event: AthenaDiagnosticEvent) => void;\n\nclass AthenaDiagnosticsBus {\n private config: AthenaDiagnosticsConfig = { ...DEFAULT_CONFIG };\n private events: AthenaDiagnosticEvent[] = [];\n private listeners = new Set<Listener>();\n private snapshotListeners = new Set<() => void>();\n private seq = 0;\n private gauges: Record<string, unknown> = {};\n private timings: Record<string, number> = {};\n private spans = new Map<string, number>();\n private cachedSnapshot: AthenaDiagnosticsSnapshot | null = null;\n\n configure(partial: Partial<AthenaDiagnosticsConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.events.length > this.config.bufferSize) {\n this.events = this.events.slice(-this.config.bufferSize);\n }\n this.invalidate();\n }\n\n getConfig(): AthenaDiagnosticsConfig {\n return this.config;\n }\n\n /** Effective console level: the configured one, or `localStorage['athena:debug']`. */\n private consoleLevel(): AthenaDiagnosticLevel | false {\n if (this.config.console !== false) return this.config.console;\n return readLocalDebugLevel() ?? false;\n }\n\n emit(\n name: AthenaDiagnosticEventName,\n options: {\n level?: AthenaDiagnosticLevel;\n data?: Record<string, unknown>;\n durationMs?: number;\n error?: unknown;\n } = {},\n ): AthenaDiagnosticEvent {\n const level = options.level ?? (options.error !== undefined ? 'error' : 'info');\n const error =\n options.error === undefined\n ? undefined\n : toAthenaSdkError(options.error, {\n code: 'unknown',\n message: `Unexpected error during ${name}`,\n });\n const event: AthenaDiagnosticEvent = {\n seq: ++this.seq,\n t: Math.round(nowMs() * 10) / 10,\n at: new Date().toISOString(),\n name,\n level,\n ...(options.durationMs !== undefined && { durationMs: Math.round(options.durationMs) }),\n ...(options.data && { data: redact(options.data) }),\n ...(error && { error }),\n };\n\n this.events.push(event);\n if (this.events.length > this.config.bufferSize) {\n this.events.splice(0, this.events.length - this.config.bufferSize);\n }\n if (event.durationMs !== undefined) {\n this.timings[name] = event.durationMs;\n }\n this.invalidate();\n\n this.echoToConsole(event);\n this.forwardToPostHog(event);\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (listenerError) {\n console.error(`${CONSOLE_PREFIX} diagnostics listener threw`, listenerError);\n }\n }\n if (this.config.onEvent) {\n try {\n this.config.onEvent(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic callback threw`, callbackError);\n }\n }\n if (error && this.config.onError) {\n try {\n this.config.onError(error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError callback threw`, callbackError);\n }\n }\n for (const notify of this.snapshotListeners) notify();\n return event;\n }\n\n /** Record an error-level event. Returns the normalized `AthenaSdkError`. */\n error(\n name: AthenaDiagnosticEventName,\n error: unknown,\n fallback: { code: AthenaSdkError['code']; message: string; hint?: string },\n data?: Record<string, unknown>,\n ): AthenaSdkError {\n const normalized = toAthenaSdkError(error, fallback);\n this.emit(name, { level: 'error', error: normalized, data });\n return normalized;\n }\n\n /**\n * Start a named span. Returns a function that ends it and emits `doneName`\n * with `durationMs` (or `errorName` when passed an error). Also records a\n * `performance.measure` so the span shows in the browser's Performance panel.\n */\n span(\n spanName: string,\n startName: AthenaDiagnosticEventName,\n data?: Record<string, unknown>,\n ): (\n doneName: AthenaDiagnosticEventName,\n doneOptions?: { data?: Record<string, unknown>; error?: unknown },\n ) => number {\n const startedAt = nowMs();\n this.spans.set(spanName, startedAt);\n this.mark(`${spanName}:start`);\n this.emit(startName, { level: 'debug', data });\n return (doneName, doneOptions = {}) => {\n const durationMs = nowMs() - startedAt;\n this.spans.delete(spanName);\n this.mark(`${spanName}:end`);\n this.measure(spanName);\n this.emit(doneName, {\n level: doneOptions.error !== undefined ? 'error' : 'info',\n durationMs,\n data: doneOptions.data,\n error: doneOptions.error,\n });\n return durationMs;\n };\n }\n\n /** Set a gauge (latest-value metric): connection status, transport, auth mode. */\n gauge(name: string, value: unknown): void {\n if (this.gauges[name] === value) return;\n this.gauges[name] = value;\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Additive consumer registration: every registered consumer receives every\n * event (and every error), unlike the single `onEvent`/`onError` config\n * slots which are last-writer-wins. `AthenaProvider` registers through this\n * so two mounted providers never clobber each other's callbacks.\n */\n addConsumer(consumer: {\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n }): () => void {\n return this.subscribe((event) => {\n try {\n consumer.onEvent?.(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic consumer threw`, callbackError);\n }\n if (event.error) {\n try {\n consumer.onError?.(event.error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError consumer threw`, callbackError);\n }\n }\n });\n }\n\n /** Subscribe to snapshot changes (for `useSyncExternalStore`). */\n subscribeSnapshot(listener: () => void): () => void {\n this.snapshotListeners.add(listener);\n return () => {\n this.snapshotListeners.delete(listener);\n };\n }\n\n snapshot(): AthenaDiagnosticsSnapshot {\n if (!this.cachedSnapshot) {\n const { onEvent: _onEvent, onError: _onError, ...config } = this.config;\n this.cachedSnapshot = {\n sdkVersion: ATHENA_REACT_SDK_VERSION,\n config,\n events: [...this.events],\n gauges: { ...this.gauges },\n timings: { ...this.timings },\n };\n }\n return this.cachedSnapshot;\n }\n\n /** Copy-paste-able JSON of the current snapshot (errors serialized). */\n export(): string {\n return JSON.stringify(\n this.snapshot(),\n (_key, value) => (isAthenaSdkError(value) ? value.toJSON() : value),\n 2,\n );\n }\n\n clear(): void {\n this.events = [];\n this.timings = {};\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n private invalidate(): void {\n this.cachedSnapshot = null;\n }\n\n private mark(name: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {\n performance.mark(`athena-sdk:${name}`);\n }\n } catch {\n // User Timing is best-effort.\n }\n }\n\n private measure(spanName: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.measure === 'function') {\n performance.measure(\n `athena-sdk:${spanName}`,\n `athena-sdk:${spanName}:start`,\n `athena-sdk:${spanName}:end`,\n );\n }\n } catch {\n // A missing start mark (e.g. after `performance.clearMarks`) is not an error.\n }\n }\n\n private echoToConsole(event: AthenaDiagnosticEvent): void {\n const level = this.consoleLevel();\n if (level === false) {\n // Errors are never silent: with console output off they still log once,\n // compactly, so a broken integration is visible without enabling debug.\n if (event.level === 'error' && event.error) {\n console.error(`${CONSOLE_PREFIX} ${event.name}: ${event.error.describe()}`, event.data ?? '');\n }\n return;\n }\n if (LEVEL_RANK[event.level] < LEVEL_RANK[level]) return;\n const suffix = event.durationMs !== undefined ? ` (${event.durationMs}ms)` : '';\n const line = `${CONSOLE_PREFIX} ${event.name}${suffix}`;\n const payload = event.error ? { ...event.data, error: event.error.describe() } : event.data;\n const method =\n event.level === 'error'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : event.level === 'debug'\n ? console.debug\n : console.info;\n method(line, payload ?? '');\n }\n\n private forwardToPostHog(event: AthenaDiagnosticEvent): void {\n if (!this.config.posthog) return;\n if (event.durationMs === undefined && event.level !== 'error') return;\n try {\n const client = getPostHogInstance<PosthogCaptureClient>();\n capturePosthogEvent(client, `athena_sdk_${event.name.replace(/\\./g, '_')}`, {\n sdk_version: ATHENA_REACT_SDK_VERSION,\n level: event.level,\n ...(event.durationMs !== undefined && { duration_ms: event.durationMs }),\n ...(event.error && {\n error_code: event.error.code,\n error_message: event.error.message,\n error_status: event.error.status,\n }),\n ...event.data,\n });\n } catch {\n // Analytics must never break the chat.\n }\n }\n}\n\nexport const athenaDiagnostics = new AthenaDiagnosticsBus();\n\n/**\n * Expose the bus on `window.__ATHENA_SDK__` so an integrator can inspect the\n * waterfall from the console (`__ATHENA_SDK__.diagnostics.snapshot()`) or turn\n * console echo on without a rebuild (`localStorage.setItem('athena:debug','debug')`).\n */\nexport function installAthenaDiagnosticsGlobal(): void {\n if (typeof window === 'undefined') return;\n const globalWindow = window as unknown as Record<string, unknown>;\n const existing = (globalWindow[GLOBAL_KEY] ?? {}) as Record<string, unknown>;\n globalWindow[GLOBAL_KEY] = {\n ...existing,\n version: ATHENA_REACT_SDK_VERSION,\n diagnostics: {\n snapshot: () => athenaDiagnostics.snapshot(),\n export: () => athenaDiagnostics.export(),\n clear: () => athenaDiagnostics.clear(),\n enableConsole: (level: AthenaDiagnosticLevel = 'debug') => {\n try {\n localStorage.setItem(LOCAL_STORAGE_DEBUG_KEY, level);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: level });\n },\n disableConsole: () => {\n try {\n localStorage.removeItem(LOCAL_STORAGE_DEBUG_KEY);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: false });\n },\n },\n };\n}\n\n/** Normalize the provider's `debug` prop into a diagnostics config patch. */\nexport function debugPropToDiagnosticsConfig(\n debug: boolean | AthenaDiagnosticLevel | Partial<AthenaDiagnosticsConfig> | undefined,\n): Partial<AthenaDiagnosticsConfig> {\n if (debug === undefined) return {};\n if (debug === true) return { console: 'debug' };\n if (debug === false) return { console: false };\n if (typeof debug === 'string') return { console: debug };\n return debug;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"athena-diagnostics.js","sources":["../../src/diagnostics/athena-diagnostics.ts"],"sourcesContent":["/**\n * SDK diagnostics: one structured event bus for everything the SDK does that\n * can be slow or can fail — auth acquisition, thread-list fetches, statewire\n * attach/snapshot/connection changes, sends, first tokens, and every error.\n *\n * Design:\n * - Module-level singleton (one SDK, one bus), configured by `AthenaProvider`\n * from its `debug` / `onDiagnostic` / `onError` props. Nothing here runs\n * until something calls `emit`, and with defaults it costs a ring-buffer push.\n * - Every event carries `t` (ms since page navigation start via\n * `performance.now()`) so the buffer reads as a waterfall. Timed spans use\n * `performance.mark` / `performance.measure` too, so the browser Performance\n * panel shows them as user timings without any extra tooling.\n * - Console output is opt-in (`debug` prop or `localStorage['athena:debug']`),\n * prefixed `[AthenaSDK]`, and level-gated. Errors always reach `onError`.\n * - PostHog forwarding (opt-in) sends the timing events as\n * `athena_sdk_<name>` with `duration_ms`, through the SDK's capture gate.\n *\n * Consumers read the buffer with `useAthenaDiagnostics()` or, in the browser\n * console, `window.__ATHENA_SDK__.diagnostics.snapshot()`.\n */\n\nimport { getPostHogInstance } from '../provider/PostHogProvider';\nimport { capturePosthogEvent, type PosthogCaptureClient } from '../lib/posthog/wrap-posthog-client';\nimport { ATHENA_REACT_SDK_VERSION } from '../version';\nimport { AthenaSdkError, isAthenaSdkError, toAthenaSdkError } from './errors';\n\nexport const ATHENA_DIAGNOSTIC_LEVELS = ['debug', 'info', 'warn', 'error'] as const;\nexport type AthenaDiagnosticLevel = (typeof ATHENA_DIAGNOSTIC_LEVELS)[number];\n\nconst LEVEL_RANK: Record<AthenaDiagnosticLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n};\n\n/**\n * Event names. Dotted `area.event` so a consumer can filter by prefix. Names\n * are part of the public contract: renaming one is a breaking change for\n * dashboards, so add new ones instead of repurposing.\n */\nexport type AthenaDiagnosticEventName =\n // Provider / configuration\n | 'sdk.init'\n | 'sdk.transport'\n | 'sdk.config.warning'\n // Auth acquisition + lifecycle\n | 'auth.bridge.start'\n | 'auth.bridge.ready'\n | 'auth.bridge.timeout'\n | 'auth.bridge.absent'\n | 'auth.bridge.error'\n | 'auth.bridge.retry'\n | 'auth.bridge.config_rejected'\n | 'auth.token.acquired'\n | 'auth.token.refreshed'\n | 'auth.token.refresh_failed'\n | 'auth.token.stale'\n | 'auth.sso.userinfo.start'\n | 'auth.sso.userinfo.done'\n | 'auth.sso.userinfo.error'\n // Thread list + history\n | 'threads.list.start'\n | 'threads.list.done'\n | 'threads.list.error'\n | 'threads.list.aborted'\n | 'threads.history.start'\n | 'threads.history.done'\n | 'threads.history.error'\n | 'threads.history.aborted'\n // Statewire transport\n | 'statewire.attach.start'\n | 'statewire.snapshot'\n | 'statewire.connection'\n | 'statewire.parked'\n | 'statewire.error'\n | 'statewire.read_only'\n // Runs\n | 'run.send'\n | 'run.first_token'\n | 'run.done'\n | 'run.error'\n // Composer\n | 'composer.attachment.error'\n | 'composer.cancel.error'\n | 'composer.dictation'\n | 'composer.handoff'\n // Tool UIs + frontend tools\n | 'tools.schema.warning'\n | 'tools.debug'\n // Mention suggestions\n | 'mentions.fetch.error'\n // Catch-all\n | 'sdk.error';\n\nexport interface AthenaDiagnosticEvent {\n /** Monotonic sequence number within this page. */\n seq: number;\n /** `performance.now()` at emit time (ms since navigation start). */\n t: number;\n /** Wall-clock ISO timestamp, for exports. */\n at: string;\n name: AthenaDiagnosticEventName;\n level: AthenaDiagnosticLevel;\n /** Milliseconds for a completed span (set on `*.done` / `*.ready` / `run.first_token`). */\n durationMs?: number;\n /** Structured context. Never contains credentials — see `redact`. */\n data?: Record<string, unknown>;\n /** Present on error-level events. */\n error?: AthenaSdkError;\n}\n\nexport interface AthenaDiagnosticsConfig {\n /** Minimum level echoed to the console. `false` disables console output. */\n console: AthenaDiagnosticLevel | false;\n /** Forward timing + error events to PostHog as `athena_sdk_*` events. */\n posthog: boolean;\n /** Receives every event (already redacted). */\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n /** Receives every error-level event's `AthenaSdkError`. */\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n /** Ring-buffer capacity. */\n bufferSize: number;\n}\n\nexport interface AthenaDiagnosticsSnapshot {\n sdkVersion: string;\n config: Omit<AthenaDiagnosticsConfig, 'onEvent' | 'onError'>;\n events: AthenaDiagnosticEvent[];\n /** Latest value per gauge (connection status, transport, auth mode, …). */\n gauges: Record<string, unknown>;\n /** Named span durations, most recent per name (`auth.bridge`, `threads.list`, …). */\n timings: Record<string, number>;\n}\n\nconst DEFAULT_CONFIG: AthenaDiagnosticsConfig = {\n console: false,\n posthog: false,\n bufferSize: 300,\n};\n\nconst CONSOLE_PREFIX = '[AthenaSDK]';\nconst LOCAL_STORAGE_DEBUG_KEY = 'athena:debug';\nconst GLOBAL_KEY = '__ATHENA_SDK__';\n\n// Keys whose values must never enter the buffer, the console, or PostHog.\nconst REDACT_KEYS = /token|secret|api[-_]?key|authorization|cookie|password/i;\n\nfunction redact(data: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n if (!data) return undefined;\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (REDACT_KEYS.test(key)) {\n out[key] = typeof value === 'string' && value.length > 0 ? `<redacted:${value.length}>` : '<redacted>';\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\nfunction nowMs(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now();\n}\n\nfunction readLocalDebugLevel(): AthenaDiagnosticLevel | null {\n try {\n if (typeof localStorage === 'undefined') return null;\n const raw = localStorage.getItem(LOCAL_STORAGE_DEBUG_KEY);\n if (!raw) return null;\n if ((ATHENA_DIAGNOSTIC_LEVELS as readonly string[]).includes(raw)) {\n return raw as AthenaDiagnosticLevel;\n }\n return raw === '1' || raw === 'true' ? 'debug' : null;\n } catch {\n return null;\n }\n}\n\ntype Listener = (event: AthenaDiagnosticEvent) => void;\n\nclass AthenaDiagnosticsBus {\n private config: AthenaDiagnosticsConfig = { ...DEFAULT_CONFIG };\n private events: AthenaDiagnosticEvent[] = [];\n private listeners = new Set<Listener>();\n private snapshotListeners = new Set<() => void>();\n private seq = 0;\n private gauges: Record<string, unknown> = {};\n private timings: Record<string, number> = {};\n private spans = new Map<string, number>();\n private cachedSnapshot: AthenaDiagnosticsSnapshot | null = null;\n\n configure(partial: Partial<AthenaDiagnosticsConfig>): void {\n this.config = { ...this.config, ...partial };\n if (this.events.length > this.config.bufferSize) {\n this.events = this.events.slice(-this.config.bufferSize);\n }\n this.invalidate();\n }\n\n getConfig(): AthenaDiagnosticsConfig {\n return this.config;\n }\n\n /** Effective console level: the configured one, or `localStorage['athena:debug']`. */\n private consoleLevel(): AthenaDiagnosticLevel | false {\n if (this.config.console !== false) return this.config.console;\n return readLocalDebugLevel() ?? false;\n }\n\n emit(\n name: AthenaDiagnosticEventName,\n options: {\n level?: AthenaDiagnosticLevel;\n data?: Record<string, unknown>;\n durationMs?: number;\n error?: unknown;\n } = {},\n ): AthenaDiagnosticEvent {\n const level = options.level ?? (options.error !== undefined ? 'error' : 'info');\n const error =\n options.error === undefined\n ? undefined\n : toAthenaSdkError(options.error, {\n code: 'unknown',\n message: `Unexpected error during ${name}`,\n });\n const event: AthenaDiagnosticEvent = {\n seq: ++this.seq,\n t: Math.round(nowMs() * 10) / 10,\n at: new Date().toISOString(),\n name,\n level,\n ...(options.durationMs !== undefined && { durationMs: Math.round(options.durationMs) }),\n ...(options.data && { data: redact(options.data) }),\n ...(error && { error }),\n };\n\n this.events.push(event);\n if (this.events.length > this.config.bufferSize) {\n this.events.splice(0, this.events.length - this.config.bufferSize);\n }\n if (event.durationMs !== undefined) {\n this.timings[name] = event.durationMs;\n }\n this.invalidate();\n\n this.echoToConsole(event);\n this.forwardToPostHog(event);\n for (const listener of this.listeners) {\n try {\n listener(event);\n } catch (listenerError) {\n console.error(`${CONSOLE_PREFIX} diagnostics listener threw`, listenerError);\n }\n }\n if (this.config.onEvent) {\n try {\n this.config.onEvent(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic callback threw`, callbackError);\n }\n }\n if (error && this.config.onError) {\n try {\n this.config.onError(error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError callback threw`, callbackError);\n }\n }\n for (const notify of this.snapshotListeners) notify();\n return event;\n }\n\n /** Record an error-level event. Returns the normalized `AthenaSdkError`. */\n error(\n name: AthenaDiagnosticEventName,\n error: unknown,\n fallback: { code: AthenaSdkError['code']; message: string; hint?: string },\n data?: Record<string, unknown>,\n ): AthenaSdkError {\n const normalized = toAthenaSdkError(error, fallback);\n this.emit(name, { level: 'error', error: normalized, data });\n return normalized;\n }\n\n /**\n * Start a named span. Returns a function that ends it and emits `doneName`\n * with `durationMs` (or `errorName` when passed an error). Also records a\n * `performance.measure` so the span shows in the browser's Performance panel.\n */\n span(\n spanName: string,\n startName: AthenaDiagnosticEventName,\n data?: Record<string, unknown>,\n ): (\n doneName: AthenaDiagnosticEventName,\n doneOptions?: { data?: Record<string, unknown>; error?: unknown },\n ) => number {\n const startedAt = nowMs();\n this.spans.set(spanName, startedAt);\n this.mark(`${spanName}:start`);\n this.emit(startName, { level: 'debug', data });\n return (doneName, doneOptions = {}) => {\n const durationMs = nowMs() - startedAt;\n this.spans.delete(spanName);\n this.mark(`${spanName}:end`);\n this.measure(spanName);\n this.emit(doneName, {\n level: doneOptions.error !== undefined ? 'error' : 'info',\n durationMs,\n data: doneOptions.data,\n error: doneOptions.error,\n });\n return durationMs;\n };\n }\n\n /** Set a gauge (latest-value metric): connection status, transport, auth mode. */\n gauge(name: string, value: unknown): void {\n if (this.gauges[name] === value) return;\n this.gauges[name] = value;\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n subscribe(listener: Listener): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n /**\n * Additive consumer registration: every registered consumer receives every\n * event (and every error), unlike the single `onEvent`/`onError` config\n * slots which are last-writer-wins. `AthenaProvider` registers through this\n * so two mounted providers never clobber each other's callbacks.\n */\n addConsumer(consumer: {\n onEvent?: (event: AthenaDiagnosticEvent) => void;\n onError?: (error: AthenaSdkError, event: AthenaDiagnosticEvent) => void;\n }): () => void {\n return this.subscribe((event) => {\n try {\n consumer.onEvent?.(event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onDiagnostic consumer threw`, callbackError);\n }\n if (event.error) {\n try {\n consumer.onError?.(event.error, event);\n } catch (callbackError) {\n console.error(`${CONSOLE_PREFIX} onError consumer threw`, callbackError);\n }\n }\n });\n }\n\n /** Subscribe to snapshot changes (for `useSyncExternalStore`). */\n subscribeSnapshot(listener: () => void): () => void {\n this.snapshotListeners.add(listener);\n return () => {\n this.snapshotListeners.delete(listener);\n };\n }\n\n snapshot(): AthenaDiagnosticsSnapshot {\n if (!this.cachedSnapshot) {\n const { onEvent: _onEvent, onError: _onError, ...config } = this.config;\n this.cachedSnapshot = {\n sdkVersion: ATHENA_REACT_SDK_VERSION,\n config,\n events: [...this.events],\n gauges: { ...this.gauges },\n timings: { ...this.timings },\n };\n }\n return this.cachedSnapshot;\n }\n\n /** Copy-paste-able JSON of the current snapshot (errors serialized). */\n export(): string {\n return JSON.stringify(\n this.snapshot(),\n (_key, value) => (isAthenaSdkError(value) ? value.toJSON() : value),\n 2,\n );\n }\n\n clear(): void {\n this.events = [];\n this.timings = {};\n this.invalidate();\n for (const notify of this.snapshotListeners) notify();\n }\n\n private invalidate(): void {\n this.cachedSnapshot = null;\n }\n\n private mark(name: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.mark === 'function') {\n performance.mark(`athena-sdk:${name}`);\n }\n } catch {\n // User Timing is best-effort.\n }\n }\n\n private measure(spanName: string): void {\n try {\n if (typeof performance !== 'undefined' && typeof performance.measure === 'function') {\n performance.measure(\n `athena-sdk:${spanName}`,\n `athena-sdk:${spanName}:start`,\n `athena-sdk:${spanName}:end`,\n );\n }\n } catch {\n // A missing start mark (e.g. after `performance.clearMarks`) is not an error.\n }\n }\n\n private echoToConsole(event: AthenaDiagnosticEvent): void {\n const level = this.consoleLevel();\n if (level === false) {\n // Errors are never silent: with console output off they still log once,\n // compactly, so a broken integration is visible without enabling debug.\n if (event.level === 'error' && event.error) {\n console.error(`${CONSOLE_PREFIX} ${event.name}: ${event.error.describe()}`, event.data ?? '');\n }\n return;\n }\n if (LEVEL_RANK[event.level] < LEVEL_RANK[level]) return;\n const suffix = event.durationMs !== undefined ? ` (${event.durationMs}ms)` : '';\n const line = `${CONSOLE_PREFIX} ${event.name}${suffix}`;\n const payload = event.error ? { ...event.data, error: event.error.describe() } : event.data;\n const method =\n event.level === 'error'\n ? console.error\n : event.level === 'warn'\n ? console.warn\n : event.level === 'debug'\n ? console.debug\n : console.info;\n method(line, payload ?? '');\n }\n\n private forwardToPostHog(event: AthenaDiagnosticEvent): void {\n if (!this.config.posthog) return;\n if (event.durationMs === undefined && event.level !== 'error') return;\n try {\n const client = getPostHogInstance<PosthogCaptureClient>();\n capturePosthogEvent(client, `athena_sdk_${event.name.replace(/\\./g, '_')}`, {\n sdk_version: ATHENA_REACT_SDK_VERSION,\n level: event.level,\n ...(event.durationMs !== undefined && { duration_ms: event.durationMs }),\n ...(event.error && {\n error_code: event.error.code,\n error_message: event.error.message,\n error_status: event.error.status,\n }),\n ...event.data,\n });\n } catch {\n // Analytics must never break the chat.\n }\n }\n}\n\nexport const athenaDiagnostics = new AthenaDiagnosticsBus();\n\n/**\n * Expose the bus on `window.__ATHENA_SDK__` so an integrator can inspect the\n * waterfall from the console (`__ATHENA_SDK__.diagnostics.snapshot()`) or turn\n * console echo on without a rebuild (`localStorage.setItem('athena:debug','debug')`).\n */\nexport function installAthenaDiagnosticsGlobal(): void {\n if (typeof window === 'undefined') return;\n const globalWindow = window as unknown as Record<string, unknown>;\n const existing = (globalWindow[GLOBAL_KEY] ?? {}) as Record<string, unknown>;\n globalWindow[GLOBAL_KEY] = {\n ...existing,\n version: ATHENA_REACT_SDK_VERSION,\n diagnostics: {\n snapshot: () => athenaDiagnostics.snapshot(),\n export: () => athenaDiagnostics.export(),\n clear: () => athenaDiagnostics.clear(),\n enableConsole: (level: AthenaDiagnosticLevel = 'debug') => {\n try {\n localStorage.setItem(LOCAL_STORAGE_DEBUG_KEY, level);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: level });\n },\n disableConsole: () => {\n try {\n localStorage.removeItem(LOCAL_STORAGE_DEBUG_KEY);\n } catch {\n // localStorage may be unavailable.\n }\n athenaDiagnostics.configure({ console: false });\n },\n },\n };\n}\n\n/** Normalize the provider's `debug` prop into a diagnostics config patch. */\nexport function debugPropToDiagnosticsConfig(\n debug: boolean | AthenaDiagnosticLevel | Partial<AthenaDiagnosticsConfig> | undefined,\n): Partial<AthenaDiagnosticsConfig> {\n if (debug === undefined) return {};\n if (debug === true) return { console: 'debug' };\n if (debug === false) return { console: false };\n if (typeof debug === 'string') return { console: debug };\n return debug;\n}\n"],"names":[],"mappings":"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"}
@@ -48,7 +48,7 @@ function assertHostMintedStatewireMount({
48
48
  }) {
49
49
  if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;
50
50
  throw new Error(
51
- "[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl): it predates the statewire-native mint. Update the host (Marathon >= 2026-08-29), or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host."
51
+ "[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl) the SDK accepted: either the host predates the statewire-native mint (update it: Marathon >= 2026-08-29), or the parent page sent a URL the SDK refused (see the auth.bridge.config_rejected diagnostics event: it must be https, on an Athena host or under the parent page's own domain). Or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host."
52
52
  );
53
53
  }
54
54
  const MARATHON_APP_PORT = "8082";
@@ -1 +1 @@
1
- {"version":3,"file":"athena-urls.cjs","sources":["../../src/lib/athena-urls.ts"],"sourcesContent":["export type AthenaEnvironment = 'production' | 'staging';\n\nexport interface AthenaEnvironmentUrls {\n apiUrl: string;\n backendUrl: string;\n appUrl: string;\n}\n\n/**\n * Bare origins: `apiUrl` is the Iris host fronting the deployment (its origin\n * derives the `/v2/threads` statewire mount) and `backendUrl` is the Agora\n * REST base. Neither carries a path — `deriveStatewireSyncUrl` and the REST\n * helpers append their own.\n */\nconst ATHENA_ENVIRONMENT_URLS: Record<AthenaEnvironment, AthenaEnvironmentUrls> = {\n production: {\n apiUrl: 'https://iris.prd.athenaintel.com',\n backendUrl: 'https://api.athenaintel.com',\n appUrl: 'https://app.athenaintel.com',\n },\n staging: {\n apiUrl: 'https://iris.stg.athenaintel.com',\n backendUrl: 'https://staging-api.athenaintel.com',\n appUrl: 'https://staging-app.athenaintel.com',\n },\n};\n\nexport const DEFAULT_ATHENA_ENVIRONMENT: AthenaEnvironment = 'production';\nexport const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;\n/** Production statewire sync endpoint — the same host the mobile app and\n * Chrome extension ship as their production statewire base. Exported as a\n * constant for explicit configuration; it is deliberately NOT used as an\n * implicit fallback (see {@link resolveStatewireSyncUrl}). */\nexport const DEFAULT_STATEWIRE_SYNC_URL = 'https://iris.prd.athenaintel.com/v2/threads';\n\nconst STATEWIRE_SYNC_PATHNAME = '/v2/threads';\n\n/**\n * Derive the statewire sync endpoint (the `…/v2/threads` mount) from the Iris\n * host's origin: staging `https://iris.stg.athenaintel.com` maps to\n * `https://iris.stg.athenaintel.com/v2/threads`, and likewise for production\n * and workspace Iris hosts (a URL still carrying the retired `/api/chat` path\n * resolves the same way). Unparseable inputs return `null` so callers can\n * fall back explicitly.\n */\nexport function deriveStatewireSyncUrl({\n apiUrl,\n}: {\n apiUrl?: string | null;\n}): string | null {\n if (!apiUrl) return null;\n\n try {\n const parsedUrl = new URL(apiUrl);\n return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the statewire sync endpoint for a provider configuration: an\n * explicit `statewireSyncUrl` wins, otherwise it is derived from the\n * effective `apiUrl` host.\n *\n * When neither yields a URL this throws instead of falling back to an Athena\n * default — mirroring `deriveAthenaApiUrl`'s null-for-unknown-hosts rule, so\n * a misconfigured custom deployment can never silently send its bearer token\n * or API key to Athena production.\n */\nexport function resolveStatewireSyncUrl({\n statewireSyncUrl,\n apiUrl,\n}: {\n statewireSyncUrl?: string;\n apiUrl?: string | null;\n}): string {\n if (statewireSyncUrl) return statewireSyncUrl;\n\n const derived = deriveStatewireSyncUrl({ apiUrl });\n if (derived) return derived;\n\n throw new Error(\n `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(\n apiUrl ?? null,\n )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`,\n );\n}\n\n/**\n * Refuse to attach a host-authenticated app to a guessed host. A host that\n * answered the auth bridge (a `/_athena/auth` mint, an `athena-config`\n * message) but minted no statewire mount predates the statewire-native\n * mint; without an explicit `statewireSyncUrl` or `apiUrl` the provider\n * would otherwise fall back to the environment preset and send that host's\n * credential to managed-cloud Athena. No host answering at all is not this\n * case: the preset is the documented default for a standalone app.\n */\nexport function assertHostMintedStatewireMount({\n hostAnswered,\n hostStatewireSyncUrl,\n statewireSyncUrl,\n apiUrl,\n}: {\n hostAnswered: boolean;\n hostStatewireSyncUrl: string | null;\n statewireSyncUrl?: string;\n apiUrl?: string;\n}): void {\n if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;\n throw new Error(\n '[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl): it predates the statewire-native mint. Update the host (Marathon >= 2026-08-29), or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host.',\n );\n}\n\nconst MARATHON_APP_PORT = '8082';\nconst SPACES_PATHNAME = 'dashboard/spaces/';\n\nconst normalizeBaseUrl = (url: string): string => url.replace(/\\/+$/, '');\n\nconst createUrlWithFallback = (url: string, fallbackUrl: string): URL => {\n try {\n return new URL(`${normalizeBaseUrl(url)}/`);\n } catch {\n return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) return null;\n\n try {\n return new URL(value).hostname;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceAppUrl = ({\n sourceUrl,\n sourcePort,\n}: {\n sourceUrl: string;\n sourcePort: '8000' | '8787';\n}): string | null => {\n try {\n const parsedUrl = new URL(sourceUrl);\n const match = parsedUrl.hostname.match(\n new RegExp(`^(?<prefix>.+)--${sourcePort}\\\\.(?<domain>app\\\\.athenaintel\\\\.com|staging-app\\\\.athenaintel\\\\.com)$`),\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--${MARATHON_APP_PORT}.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceApiUrl = (backendUrl: string): string | null => {\n try {\n const parsedUrl = new URL(backendUrl);\n const match = parsedUrl.hostname.match(\n /^(?<prefix>.+)--8000\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/,\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--8787.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nexport function getAthenaEnvironmentUrls({\n environment = DEFAULT_ATHENA_ENVIRONMENT,\n}: {\n environment?: AthenaEnvironment;\n} = {}): AthenaEnvironmentUrls {\n return ATHENA_ENVIRONMENT_URLS[environment];\n}\n\n/**\n * Resolve the Iris origin that fronts a known Agora backend.\n *\n * Unknown custom backends deliberately return `null`: callers can preserve a\n * direct-Agora fallback instead of accidentally sending a custom environment's\n * bearer token and traffic through production Iris.\n */\nexport function deriveAthenaApiUrl({\n backendUrl,\n}: {\n backendUrl?: string | null;\n}): string | null {\n if (!backendUrl) return null;\n\n const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);\n if (workspaceApiUrl) return workspaceApiUrl;\n\n try {\n const parsedUrl = new URL(backendUrl);\n if (parsedUrl.hostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.apiUrl;\n }\n if (parsedUrl.hostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;\n }\n if (parsedUrl.hostname === 'localhost' || parsedUrl.hostname === '127.0.0.1') {\n parsedUrl.port = '8787';\n return parsedUrl.origin;\n }\n } catch {\n return null;\n }\n\n return null;\n}\n\nexport function deriveAthenaAppUrl({\n apiUrl,\n backendUrl,\n}: {\n apiUrl?: string | null;\n backendUrl?: string | null;\n}): string | null {\n if (backendUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: backendUrl,\n sourcePort: '8000',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const backendHostname = getHostname(backendUrl);\n if (backendHostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (backendHostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n if (apiUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: apiUrl,\n sourcePort: '8787',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const apiHostname = getHostname(apiUrl);\n if (apiHostname === 'iris.prd.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (apiHostname === 'iris.stg.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n return null;\n}\n\nexport function createAthenaSpacesUrl({\n appUrl = DEFAULT_APP_URL,\n assetIds,\n sessionId,\n}: {\n appUrl?: string;\n assetIds?: string | string[];\n sessionId?: string;\n}): string {\n const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));\n\n if (assetIds) {\n const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds])\n .map((assetId) => assetId.trim())\n .filter((assetId) => assetId.length > 0);\n\n if (normalizedAssetIds.length > 0) {\n url.searchParams.set('asset_ids', normalizedAssetIds.join(','));\n }\n }\n\n if (sessionId) {\n url.searchParams.set('session_id', sessionId);\n }\n\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"athena-urls.cjs","sources":["../../src/lib/athena-urls.ts"],"sourcesContent":["export type AthenaEnvironment = 'production' | 'staging';\n\nexport interface AthenaEnvironmentUrls {\n apiUrl: string;\n backendUrl: string;\n appUrl: string;\n}\n\n/**\n * Bare origins: `apiUrl` is the Iris host fronting the deployment (its origin\n * derives the `/v2/threads` statewire mount) and `backendUrl` is the Agora\n * REST base. Neither carries a path — `deriveStatewireSyncUrl` and the REST\n * helpers append their own.\n */\nconst ATHENA_ENVIRONMENT_URLS: Record<AthenaEnvironment, AthenaEnvironmentUrls> = {\n production: {\n apiUrl: 'https://iris.prd.athenaintel.com',\n backendUrl: 'https://api.athenaintel.com',\n appUrl: 'https://app.athenaintel.com',\n },\n staging: {\n apiUrl: 'https://iris.stg.athenaintel.com',\n backendUrl: 'https://staging-api.athenaintel.com',\n appUrl: 'https://staging-app.athenaintel.com',\n },\n};\n\nexport const DEFAULT_ATHENA_ENVIRONMENT: AthenaEnvironment = 'production';\nexport const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;\n/** Production statewire sync endpoint — the same host the mobile app and\n * Chrome extension ship as their production statewire base. Exported as a\n * constant for explicit configuration; it is deliberately NOT used as an\n * implicit fallback (see {@link resolveStatewireSyncUrl}). */\nexport const DEFAULT_STATEWIRE_SYNC_URL = 'https://iris.prd.athenaintel.com/v2/threads';\n\nconst STATEWIRE_SYNC_PATHNAME = '/v2/threads';\n\n/**\n * Derive the statewire sync endpoint (the `…/v2/threads` mount) from the Iris\n * host's origin: staging `https://iris.stg.athenaintel.com` maps to\n * `https://iris.stg.athenaintel.com/v2/threads`, and likewise for production\n * and workspace Iris hosts (a URL still carrying the retired `/api/chat` path\n * resolves the same way). Unparseable inputs return `null` so callers can\n * fall back explicitly.\n */\nexport function deriveStatewireSyncUrl({\n apiUrl,\n}: {\n apiUrl?: string | null;\n}): string | null {\n if (!apiUrl) return null;\n\n try {\n const parsedUrl = new URL(apiUrl);\n return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the statewire sync endpoint for a provider configuration: an\n * explicit `statewireSyncUrl` wins, otherwise it is derived from the\n * effective `apiUrl` host.\n *\n * When neither yields a URL this throws instead of falling back to an Athena\n * default — mirroring `deriveAthenaApiUrl`'s null-for-unknown-hosts rule, so\n * a misconfigured custom deployment can never silently send its bearer token\n * or API key to Athena production.\n */\nexport function resolveStatewireSyncUrl({\n statewireSyncUrl,\n apiUrl,\n}: {\n statewireSyncUrl?: string;\n apiUrl?: string | null;\n}): string {\n if (statewireSyncUrl) return statewireSyncUrl;\n\n const derived = deriveStatewireSyncUrl({ apiUrl });\n if (derived) return derived;\n\n throw new Error(\n `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(\n apiUrl ?? null,\n )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`,\n );\n}\n\n/**\n * Refuse to attach a host-authenticated app to a guessed host. A host that\n * answered the auth bridge (a `/_athena/auth` mint, an `athena-config`\n * message) but minted no statewire mount predates the statewire-native\n * mint; without an explicit `statewireSyncUrl` or `apiUrl` the provider\n * would otherwise fall back to the environment preset and send that host's\n * credential to managed-cloud Athena. No host answering at all is not this\n * case: the preset is the documented default for a standalone app.\n */\nexport function assertHostMintedStatewireMount({\n hostAnswered,\n hostStatewireSyncUrl,\n statewireSyncUrl,\n apiUrl,\n}: {\n hostAnswered: boolean;\n hostStatewireSyncUrl: string | null;\n statewireSyncUrl?: string;\n apiUrl?: string;\n}): void {\n if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;\n throw new Error(\n '[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl) the SDK accepted: either the host predates the statewire-native mint (update it: Marathon >= 2026-08-29), or the parent page sent a URL the SDK refused (see the auth.bridge.config_rejected diagnostics event: it must be https, on an Athena host or under the parent page\\'s own domain). Or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host.',\n );\n}\n\nconst MARATHON_APP_PORT = '8082';\nconst SPACES_PATHNAME = 'dashboard/spaces/';\n\nconst normalizeBaseUrl = (url: string): string => url.replace(/\\/+$/, '');\n\nconst createUrlWithFallback = (url: string, fallbackUrl: string): URL => {\n try {\n return new URL(`${normalizeBaseUrl(url)}/`);\n } catch {\n return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) return null;\n\n try {\n return new URL(value).hostname;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceAppUrl = ({\n sourceUrl,\n sourcePort,\n}: {\n sourceUrl: string;\n sourcePort: '8000' | '8787';\n}): string | null => {\n try {\n const parsedUrl = new URL(sourceUrl);\n const match = parsedUrl.hostname.match(\n new RegExp(`^(?<prefix>.+)--${sourcePort}\\\\.(?<domain>app\\\\.athenaintel\\\\.com|staging-app\\\\.athenaintel\\\\.com)$`),\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--${MARATHON_APP_PORT}.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceApiUrl = (backendUrl: string): string | null => {\n try {\n const parsedUrl = new URL(backendUrl);\n const match = parsedUrl.hostname.match(\n /^(?<prefix>.+)--8000\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/,\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--8787.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nexport function getAthenaEnvironmentUrls({\n environment = DEFAULT_ATHENA_ENVIRONMENT,\n}: {\n environment?: AthenaEnvironment;\n} = {}): AthenaEnvironmentUrls {\n return ATHENA_ENVIRONMENT_URLS[environment];\n}\n\n/**\n * Resolve the Iris origin that fronts a known Agora backend.\n *\n * Unknown custom backends deliberately return `null`: callers can preserve a\n * direct-Agora fallback instead of accidentally sending a custom environment's\n * bearer token and traffic through production Iris.\n */\nexport function deriveAthenaApiUrl({\n backendUrl,\n}: {\n backendUrl?: string | null;\n}): string | null {\n if (!backendUrl) return null;\n\n const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);\n if (workspaceApiUrl) return workspaceApiUrl;\n\n try {\n const parsedUrl = new URL(backendUrl);\n if (parsedUrl.hostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.apiUrl;\n }\n if (parsedUrl.hostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;\n }\n if (parsedUrl.hostname === 'localhost' || parsedUrl.hostname === '127.0.0.1') {\n parsedUrl.port = '8787';\n return parsedUrl.origin;\n }\n } catch {\n return null;\n }\n\n return null;\n}\n\nexport function deriveAthenaAppUrl({\n apiUrl,\n backendUrl,\n}: {\n apiUrl?: string | null;\n backendUrl?: string | null;\n}): string | null {\n if (backendUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: backendUrl,\n sourcePort: '8000',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const backendHostname = getHostname(backendUrl);\n if (backendHostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (backendHostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n if (apiUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: apiUrl,\n sourcePort: '8787',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const apiHostname = getHostname(apiUrl);\n if (apiHostname === 'iris.prd.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (apiHostname === 'iris.stg.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n return null;\n}\n\nexport function createAthenaSpacesUrl({\n appUrl = DEFAULT_APP_URL,\n assetIds,\n sessionId,\n}: {\n appUrl?: string;\n assetIds?: string | string[];\n sessionId?: string;\n}): string {\n const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));\n\n if (assetIds) {\n const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds])\n .map((assetId) => assetId.trim())\n .filter((assetId) => assetId.length > 0);\n\n if (normalizedAssetIds.length > 0) {\n url.searchParams.set('asset_ids', normalizedAssetIds.join(','));\n }\n }\n\n if (sessionId) {\n url.searchParams.set('session_id', sessionId);\n }\n\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
@@ -46,7 +46,7 @@ function assertHostMintedStatewireMount({
46
46
  }) {
47
47
  if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;
48
48
  throw new Error(
49
- "[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl): it predates the statewire-native mint. Update the host (Marathon >= 2026-08-29), or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host."
49
+ "[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl) the SDK accepted: either the host predates the statewire-native mint (update it: Marathon >= 2026-08-29), or the parent page sent a URL the SDK refused (see the auth.bridge.config_rejected diagnostics event: it must be https, on an Athena host or under the parent page's own domain). Or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host."
50
50
  );
51
51
  }
52
52
  const MARATHON_APP_PORT = "8082";
@@ -1 +1 @@
1
- {"version":3,"file":"athena-urls.js","sources":["../../src/lib/athena-urls.ts"],"sourcesContent":["export type AthenaEnvironment = 'production' | 'staging';\n\nexport interface AthenaEnvironmentUrls {\n apiUrl: string;\n backendUrl: string;\n appUrl: string;\n}\n\n/**\n * Bare origins: `apiUrl` is the Iris host fronting the deployment (its origin\n * derives the `/v2/threads` statewire mount) and `backendUrl` is the Agora\n * REST base. Neither carries a path — `deriveStatewireSyncUrl` and the REST\n * helpers append their own.\n */\nconst ATHENA_ENVIRONMENT_URLS: Record<AthenaEnvironment, AthenaEnvironmentUrls> = {\n production: {\n apiUrl: 'https://iris.prd.athenaintel.com',\n backendUrl: 'https://api.athenaintel.com',\n appUrl: 'https://app.athenaintel.com',\n },\n staging: {\n apiUrl: 'https://iris.stg.athenaintel.com',\n backendUrl: 'https://staging-api.athenaintel.com',\n appUrl: 'https://staging-app.athenaintel.com',\n },\n};\n\nexport const DEFAULT_ATHENA_ENVIRONMENT: AthenaEnvironment = 'production';\nexport const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;\n/** Production statewire sync endpoint — the same host the mobile app and\n * Chrome extension ship as their production statewire base. Exported as a\n * constant for explicit configuration; it is deliberately NOT used as an\n * implicit fallback (see {@link resolveStatewireSyncUrl}). */\nexport const DEFAULT_STATEWIRE_SYNC_URL = 'https://iris.prd.athenaintel.com/v2/threads';\n\nconst STATEWIRE_SYNC_PATHNAME = '/v2/threads';\n\n/**\n * Derive the statewire sync endpoint (the `…/v2/threads` mount) from the Iris\n * host's origin: staging `https://iris.stg.athenaintel.com` maps to\n * `https://iris.stg.athenaintel.com/v2/threads`, and likewise for production\n * and workspace Iris hosts (a URL still carrying the retired `/api/chat` path\n * resolves the same way). Unparseable inputs return `null` so callers can\n * fall back explicitly.\n */\nexport function deriveStatewireSyncUrl({\n apiUrl,\n}: {\n apiUrl?: string | null;\n}): string | null {\n if (!apiUrl) return null;\n\n try {\n const parsedUrl = new URL(apiUrl);\n return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the statewire sync endpoint for a provider configuration: an\n * explicit `statewireSyncUrl` wins, otherwise it is derived from the\n * effective `apiUrl` host.\n *\n * When neither yields a URL this throws instead of falling back to an Athena\n * default — mirroring `deriveAthenaApiUrl`'s null-for-unknown-hosts rule, so\n * a misconfigured custom deployment can never silently send its bearer token\n * or API key to Athena production.\n */\nexport function resolveStatewireSyncUrl({\n statewireSyncUrl,\n apiUrl,\n}: {\n statewireSyncUrl?: string;\n apiUrl?: string | null;\n}): string {\n if (statewireSyncUrl) return statewireSyncUrl;\n\n const derived = deriveStatewireSyncUrl({ apiUrl });\n if (derived) return derived;\n\n throw new Error(\n `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(\n apiUrl ?? null,\n )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`,\n );\n}\n\n/**\n * Refuse to attach a host-authenticated app to a guessed host. A host that\n * answered the auth bridge (a `/_athena/auth` mint, an `athena-config`\n * message) but minted no statewire mount predates the statewire-native\n * mint; without an explicit `statewireSyncUrl` or `apiUrl` the provider\n * would otherwise fall back to the environment preset and send that host's\n * credential to managed-cloud Athena. No host answering at all is not this\n * case: the preset is the documented default for a standalone app.\n */\nexport function assertHostMintedStatewireMount({\n hostAnswered,\n hostStatewireSyncUrl,\n statewireSyncUrl,\n apiUrl,\n}: {\n hostAnswered: boolean;\n hostStatewireSyncUrl: string | null;\n statewireSyncUrl?: string;\n apiUrl?: string;\n}): void {\n if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;\n throw new Error(\n '[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl): it predates the statewire-native mint. Update the host (Marathon >= 2026-08-29), or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host.',\n );\n}\n\nconst MARATHON_APP_PORT = '8082';\nconst SPACES_PATHNAME = 'dashboard/spaces/';\n\nconst normalizeBaseUrl = (url: string): string => url.replace(/\\/+$/, '');\n\nconst createUrlWithFallback = (url: string, fallbackUrl: string): URL => {\n try {\n return new URL(`${normalizeBaseUrl(url)}/`);\n } catch {\n return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) return null;\n\n try {\n return new URL(value).hostname;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceAppUrl = ({\n sourceUrl,\n sourcePort,\n}: {\n sourceUrl: string;\n sourcePort: '8000' | '8787';\n}): string | null => {\n try {\n const parsedUrl = new URL(sourceUrl);\n const match = parsedUrl.hostname.match(\n new RegExp(`^(?<prefix>.+)--${sourcePort}\\\\.(?<domain>app\\\\.athenaintel\\\\.com|staging-app\\\\.athenaintel\\\\.com)$`),\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--${MARATHON_APP_PORT}.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceApiUrl = (backendUrl: string): string | null => {\n try {\n const parsedUrl = new URL(backendUrl);\n const match = parsedUrl.hostname.match(\n /^(?<prefix>.+)--8000\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/,\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--8787.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nexport function getAthenaEnvironmentUrls({\n environment = DEFAULT_ATHENA_ENVIRONMENT,\n}: {\n environment?: AthenaEnvironment;\n} = {}): AthenaEnvironmentUrls {\n return ATHENA_ENVIRONMENT_URLS[environment];\n}\n\n/**\n * Resolve the Iris origin that fronts a known Agora backend.\n *\n * Unknown custom backends deliberately return `null`: callers can preserve a\n * direct-Agora fallback instead of accidentally sending a custom environment's\n * bearer token and traffic through production Iris.\n */\nexport function deriveAthenaApiUrl({\n backendUrl,\n}: {\n backendUrl?: string | null;\n}): string | null {\n if (!backendUrl) return null;\n\n const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);\n if (workspaceApiUrl) return workspaceApiUrl;\n\n try {\n const parsedUrl = new URL(backendUrl);\n if (parsedUrl.hostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.apiUrl;\n }\n if (parsedUrl.hostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;\n }\n if (parsedUrl.hostname === 'localhost' || parsedUrl.hostname === '127.0.0.1') {\n parsedUrl.port = '8787';\n return parsedUrl.origin;\n }\n } catch {\n return null;\n }\n\n return null;\n}\n\nexport function deriveAthenaAppUrl({\n apiUrl,\n backendUrl,\n}: {\n apiUrl?: string | null;\n backendUrl?: string | null;\n}): string | null {\n if (backendUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: backendUrl,\n sourcePort: '8000',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const backendHostname = getHostname(backendUrl);\n if (backendHostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (backendHostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n if (apiUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: apiUrl,\n sourcePort: '8787',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const apiHostname = getHostname(apiUrl);\n if (apiHostname === 'iris.prd.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (apiHostname === 'iris.stg.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n return null;\n}\n\nexport function createAthenaSpacesUrl({\n appUrl = DEFAULT_APP_URL,\n assetIds,\n sessionId,\n}: {\n appUrl?: string;\n assetIds?: string | string[];\n sessionId?: string;\n}): string {\n const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));\n\n if (assetIds) {\n const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds])\n .map((assetId) => assetId.trim())\n .filter((assetId) => assetId.length > 0);\n\n if (normalizedAssetIds.length > 0) {\n url.searchParams.set('asset_ids', normalizedAssetIds.join(','));\n }\n }\n\n if (sessionId) {\n url.searchParams.set('session_id', sessionId);\n }\n\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"athena-urls.js","sources":["../../src/lib/athena-urls.ts"],"sourcesContent":["export type AthenaEnvironment = 'production' | 'staging';\n\nexport interface AthenaEnvironmentUrls {\n apiUrl: string;\n backendUrl: string;\n appUrl: string;\n}\n\n/**\n * Bare origins: `apiUrl` is the Iris host fronting the deployment (its origin\n * derives the `/v2/threads` statewire mount) and `backendUrl` is the Agora\n * REST base. Neither carries a path — `deriveStatewireSyncUrl` and the REST\n * helpers append their own.\n */\nconst ATHENA_ENVIRONMENT_URLS: Record<AthenaEnvironment, AthenaEnvironmentUrls> = {\n production: {\n apiUrl: 'https://iris.prd.athenaintel.com',\n backendUrl: 'https://api.athenaintel.com',\n appUrl: 'https://app.athenaintel.com',\n },\n staging: {\n apiUrl: 'https://iris.stg.athenaintel.com',\n backendUrl: 'https://staging-api.athenaintel.com',\n appUrl: 'https://staging-app.athenaintel.com',\n },\n};\n\nexport const DEFAULT_ATHENA_ENVIRONMENT: AthenaEnvironment = 'production';\nexport const DEFAULT_APP_URL = ATHENA_ENVIRONMENT_URLS.production.appUrl;\n/** Production statewire sync endpoint — the same host the mobile app and\n * Chrome extension ship as their production statewire base. Exported as a\n * constant for explicit configuration; it is deliberately NOT used as an\n * implicit fallback (see {@link resolveStatewireSyncUrl}). */\nexport const DEFAULT_STATEWIRE_SYNC_URL = 'https://iris.prd.athenaintel.com/v2/threads';\n\nconst STATEWIRE_SYNC_PATHNAME = '/v2/threads';\n\n/**\n * Derive the statewire sync endpoint (the `…/v2/threads` mount) from the Iris\n * host's origin: staging `https://iris.stg.athenaintel.com` maps to\n * `https://iris.stg.athenaintel.com/v2/threads`, and likewise for production\n * and workspace Iris hosts (a URL still carrying the retired `/api/chat` path\n * resolves the same way). Unparseable inputs return `null` so callers can\n * fall back explicitly.\n */\nexport function deriveStatewireSyncUrl({\n apiUrl,\n}: {\n apiUrl?: string | null;\n}): string | null {\n if (!apiUrl) return null;\n\n try {\n const parsedUrl = new URL(apiUrl);\n return `${parsedUrl.origin}${STATEWIRE_SYNC_PATHNAME}`;\n } catch {\n return null;\n }\n}\n\n/**\n * Resolve the statewire sync endpoint for a provider configuration: an\n * explicit `statewireSyncUrl` wins, otherwise it is derived from the\n * effective `apiUrl` host.\n *\n * When neither yields a URL this throws instead of falling back to an Athena\n * default — mirroring `deriveAthenaApiUrl`'s null-for-unknown-hosts rule, so\n * a misconfigured custom deployment can never silently send its bearer token\n * or API key to Athena production.\n */\nexport function resolveStatewireSyncUrl({\n statewireSyncUrl,\n apiUrl,\n}: {\n statewireSyncUrl?: string;\n apiUrl?: string | null;\n}): string {\n if (statewireSyncUrl) return statewireSyncUrl;\n\n const derived = deriveStatewireSyncUrl({ apiUrl });\n if (derived) return derived;\n\n throw new Error(\n `[AthenaSDK] transport 'statewire' could not derive a sync URL from apiUrl (${JSON.stringify(\n apiUrl ?? null,\n )}). Pass an explicit statewireSyncUrl (the …/v2/threads mount). Credentials are never sent to a default host.`,\n );\n}\n\n/**\n * Refuse to attach a host-authenticated app to a guessed host. A host that\n * answered the auth bridge (a `/_athena/auth` mint, an `athena-config`\n * message) but minted no statewire mount predates the statewire-native\n * mint; without an explicit `statewireSyncUrl` or `apiUrl` the provider\n * would otherwise fall back to the environment preset and send that host's\n * credential to managed-cloud Athena. No host answering at all is not this\n * case: the preset is the documented default for a standalone app.\n */\nexport function assertHostMintedStatewireMount({\n hostAnswered,\n hostStatewireSyncUrl,\n statewireSyncUrl,\n apiUrl,\n}: {\n hostAnswered: boolean;\n hostStatewireSyncUrl: string | null;\n statewireSyncUrl?: string;\n apiUrl?: string;\n}): void {\n if (!hostAnswered || hostStatewireSyncUrl || statewireSyncUrl || apiUrl) return;\n throw new Error(\n '[AthenaSDK] The host answered the auth bridge without a statewire sync mount (statewireSyncUrl) the SDK accepted: either the host predates the statewire-native mint (update it: Marathon >= 2026-08-29), or the parent page sent a URL the SDK refused (see the auth.bridge.config_rejected diagnostics event: it must be https, on an Athena host or under the parent page\\'s own domain). Or configure statewireSyncUrl / apiUrl explicitly. Credentials are never sent to a default host.',\n );\n}\n\nconst MARATHON_APP_PORT = '8082';\nconst SPACES_PATHNAME = 'dashboard/spaces/';\n\nconst normalizeBaseUrl = (url: string): string => url.replace(/\\/+$/, '');\n\nconst createUrlWithFallback = (url: string, fallbackUrl: string): URL => {\n try {\n return new URL(`${normalizeBaseUrl(url)}/`);\n } catch {\n return new URL(`${normalizeBaseUrl(fallbackUrl)}/`);\n }\n};\n\nconst getHostname = (value?: string | null): string | null => {\n if (!value) return null;\n\n try {\n return new URL(value).hostname;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceAppUrl = ({\n sourceUrl,\n sourcePort,\n}: {\n sourceUrl: string;\n sourcePort: '8000' | '8787';\n}): string | null => {\n try {\n const parsedUrl = new URL(sourceUrl);\n const match = parsedUrl.hostname.match(\n new RegExp(`^(?<prefix>.+)--${sourcePort}\\\\.(?<domain>app\\\\.athenaintel\\\\.com|staging-app\\\\.athenaintel\\\\.com)$`),\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--${MARATHON_APP_PORT}.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nconst deriveWorkspaceApiUrl = (backendUrl: string): string | null => {\n try {\n const parsedUrl = new URL(backendUrl);\n const match = parsedUrl.hostname.match(\n /^(?<prefix>.+)--8000\\.(?<domain>app\\.athenaintel\\.com|staging-app\\.athenaintel\\.com)$/,\n );\n\n if (!match?.groups?.prefix || !match.groups.domain) {\n return null;\n }\n\n return `${parsedUrl.protocol}//${match.groups.prefix}--8787.${match.groups.domain}`;\n } catch {\n return null;\n }\n};\n\nexport function getAthenaEnvironmentUrls({\n environment = DEFAULT_ATHENA_ENVIRONMENT,\n}: {\n environment?: AthenaEnvironment;\n} = {}): AthenaEnvironmentUrls {\n return ATHENA_ENVIRONMENT_URLS[environment];\n}\n\n/**\n * Resolve the Iris origin that fronts a known Agora backend.\n *\n * Unknown custom backends deliberately return `null`: callers can preserve a\n * direct-Agora fallback instead of accidentally sending a custom environment's\n * bearer token and traffic through production Iris.\n */\nexport function deriveAthenaApiUrl({\n backendUrl,\n}: {\n backendUrl?: string | null;\n}): string | null {\n if (!backendUrl) return null;\n\n const workspaceApiUrl = deriveWorkspaceApiUrl(backendUrl);\n if (workspaceApiUrl) return workspaceApiUrl;\n\n try {\n const parsedUrl = new URL(backendUrl);\n if (parsedUrl.hostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.apiUrl;\n }\n if (parsedUrl.hostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.apiUrl;\n }\n if (parsedUrl.hostname === 'localhost' || parsedUrl.hostname === '127.0.0.1') {\n parsedUrl.port = '8787';\n return parsedUrl.origin;\n }\n } catch {\n return null;\n }\n\n return null;\n}\n\nexport function deriveAthenaAppUrl({\n apiUrl,\n backendUrl,\n}: {\n apiUrl?: string | null;\n backendUrl?: string | null;\n}): string | null {\n if (backendUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: backendUrl,\n sourcePort: '8000',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const backendHostname = getHostname(backendUrl);\n if (backendHostname === 'api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (backendHostname === 'staging-api.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n if (apiUrl) {\n const workspaceAppUrl = deriveWorkspaceAppUrl({\n sourceUrl: apiUrl,\n sourcePort: '8787',\n });\n if (workspaceAppUrl) return workspaceAppUrl;\n\n const apiHostname = getHostname(apiUrl);\n if (apiHostname === 'iris.prd.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.production.appUrl;\n }\n if (apiHostname === 'iris.stg.athenaintel.com') {\n return ATHENA_ENVIRONMENT_URLS.staging.appUrl;\n }\n }\n\n return null;\n}\n\nexport function createAthenaSpacesUrl({\n appUrl = DEFAULT_APP_URL,\n assetIds,\n sessionId,\n}: {\n appUrl?: string;\n assetIds?: string | string[];\n sessionId?: string;\n}): string {\n const url = new URL(SPACES_PATHNAME, createUrlWithFallback(appUrl, DEFAULT_APP_URL));\n\n if (assetIds) {\n const normalizedAssetIds = (Array.isArray(assetIds) ? assetIds : [assetIds])\n .map((assetId) => assetId.trim())\n .filter((assetId) => assetId.length > 0);\n\n if (normalizedAssetIds.length > 0) {\n url.searchParams.set('asset_ids', normalizedAssetIds.join(','));\n }\n }\n\n if (sessionId) {\n url.searchParams.set('session_id', sessionId);\n }\n\n return url.toString();\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const SENSITIVE_FIELD_HINT = /password|passphrase|passcode|secret|token|api[\s_-]?key|private[\s_-]?key|access[\s_-]?key|credential|bearer|one-time-code|\b(otp|pat|ssn|cvc|cvv)\b/i;
4
+ const SENSITIVE_VALUE_PATTERN_LIST = [
5
+ /-----BEGIN [A-Z ]+-----/,
6
+ /\beyJ[\w-]{8,}\.[\w-]{8,}\./,
7
+ /"private_key"\s*:/,
8
+ /\b(sk|pk|rk)[-_][\w-]{16,}/,
9
+ /\b(ghp|gho|ghu|ghs|ghr|github_pat)_[A-Za-z0-9_]{20,}/,
10
+ /\bxox[abposr]-[\w-]{10,}/,
11
+ /\bAKIA[0-9A-Z]{16}\b/,
12
+ /\bAIza[\w-]{35}\b/
13
+ ];
14
+ const HINT_ATTRIBUTE_LIST = ["type", "name", "id", "autocomplete", "placeholder", "aria-label"];
15
+ function fieldHintText(element) {
16
+ const attributes = HINT_ATTRIBUTE_LIST.map((name) => element.getAttribute(name) ?? "");
17
+ const labels = "labels" in element && element.labels instanceof NodeList ? Array.from(element.labels, (label) => label.textContent ?? "") : [];
18
+ return [...attributes, ...labels].join(" ");
19
+ }
20
+ function isSensitiveInput({
21
+ text,
22
+ element
23
+ }) {
24
+ if (SENSITIVE_VALUE_PATTERN_LIST.some((pattern) => pattern.test(text))) return true;
25
+ if (!element) return false;
26
+ if (element.hasAttribute("data-rr-is-password") || element.closest(".ph-mask")) return true;
27
+ return SENSITIVE_FIELD_HINT.test(fieldHintText(element));
28
+ }
29
+ function maskSensitiveInput(text, element) {
30
+ return isSensitiveInput({ text, element }) ? "*".repeat(text.length) : text;
31
+ }
32
+ exports.isSensitiveInput = isSensitiveInput;
33
+ exports.maskSensitiveInput = maskSensitiveInput;
34
+ //# sourceMappingURL=mask-sensitive-input.cjs.map