@athenaintel/react 0.55.0 → 0.57.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.
- package/README.md +84 -1
- package/dist/agent-runtime/src/answer-label.cjs +8 -2
- package/dist/agent-runtime/src/answer-label.cjs.map +1 -1
- package/dist/agent-runtime/src/answer-label.d.ts +10 -4
- package/dist/agent-runtime/src/answer-label.js +8 -2
- package/dist/agent-runtime/src/answer-label.js.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs +6 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.cjs.map +1 -1
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js +6 -3
- package/dist/agent-runtime/src/deep-agent-transport-recipe.js.map +1 -1
- package/dist/agent-runtime/src/edit-command.cjs +40 -19
- package/dist/agent-runtime/src/edit-command.cjs.map +1 -1
- package/dist/agent-runtime/src/edit-command.d.ts +2 -0
- package/dist/agent-runtime/src/edit-command.js +41 -20
- package/dist/agent-runtime/src/edit-command.js.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs +8 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.cjs.map +1 -1
- package/dist/agent-runtime/src/generated/runconfig-contract.d.ts +9 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js +8 -0
- package/dist/agent-runtime/src/generated/runconfig-contract.js.map +1 -1
- package/dist/agent-runtime/src/history-store.cjs +426 -0
- package/dist/agent-runtime/src/history-store.cjs.map +1 -0
- package/dist/agent-runtime/src/history-store.d.ts +282 -0
- package/dist/agent-runtime/src/history-store.js +426 -0
- package/dist/agent-runtime/src/history-store.js.map +1 -0
- package/dist/agent-runtime/src/inbound-channel.cjs +96 -0
- package/dist/agent-runtime/src/inbound-channel.cjs.map +1 -0
- package/dist/agent-runtime/src/inbound-channel.d.ts +75 -0
- package/dist/agent-runtime/src/inbound-channel.js +96 -0
- package/dist/agent-runtime/src/inbound-channel.js.map +1 -0
- package/dist/agent-runtime/src/index.d.ts +6 -5
- package/dist/agent-runtime/src/interrupts.cjs +45 -3
- package/dist/agent-runtime/src/interrupts.cjs.map +1 -1
- package/dist/agent-runtime/src/interrupts.d.ts +51 -4
- package/dist/agent-runtime/src/interrupts.js +46 -4
- package/dist/agent-runtime/src/interrupts.js.map +1 -1
- package/dist/agent-runtime/src/message-window.cjs +43 -5
- package/dist/agent-runtime/src/message-window.cjs.map +1 -1
- package/dist/agent-runtime/src/message-window.d.ts +26 -2
- package/dist/agent-runtime/src/message-window.js +44 -6
- package/dist/agent-runtime/src/message-window.js.map +1 -1
- package/dist/agent-runtime/src/offline-status.cjs.map +1 -1
- package/dist/agent-runtime/src/offline-status.d.ts +110 -4
- package/dist/agent-runtime/src/offline-status.js.map +1 -1
- package/dist/agent-runtime/src/runtime.d.ts +1 -0
- package/dist/agent-runtime/src/thread-handoff-flow-machine.d.ts +8 -8
- package/dist/agent-runtime/src/tool-metadata.cjs +16 -0
- package/dist/agent-runtime/src/tool-metadata.cjs.map +1 -1
- package/dist/agent-runtime/src/tool-metadata.js +16 -0
- package/dist/agent-runtime/src/tool-metadata.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs +8 -16
- package/dist/agent-runtime/src/use-deep-agent-thread.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-thread.d.ts +1 -9
- package/dist/agent-runtime/src/use-deep-agent-thread.js +9 -17
- package/dist/agent-runtime/src/use-deep-agent-thread.js.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs +86 -79
- package/dist/agent-runtime/src/use-deep-agent-window-sync.cjs.map +1 -1
- package/dist/agent-runtime/src/use-deep-agent-window-sync.d.ts +2 -27
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js +89 -82
- package/dist/agent-runtime/src/use-deep-agent-window-sync.js.map +1 -1
- package/dist/auth/AthenaSSOProvider.cjs +54 -12
- package/dist/auth/AthenaSSOProvider.cjs.map +1 -1
- package/dist/auth/AthenaSSOProvider.d.ts +23 -2
- package/dist/auth/AthenaSSOProvider.js +54 -12
- package/dist/auth/AthenaSSOProvider.js.map +1 -1
- package/dist/auth/index.cjs +2 -0
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/sign-back-in.cjs +141 -0
- package/dist/auth/sign-back-in.cjs.map +1 -0
- package/dist/auth/sign-back-in.d.ts +117 -0
- package/dist/auth/sign-back-in.js +141 -0
- package/dist/auth/sign-back-in.js.map +1 -0
- package/dist/chat/AthenaChat.cjs +7 -2
- package/dist/chat/AthenaChat.cjs.map +1 -1
- package/dist/chat/AthenaChat.js +7 -2
- package/dist/chat/AthenaChat.js.map +1 -1
- package/dist/chat/StatewireApprovalCard.cjs +6 -2
- package/dist/chat/StatewireApprovalCard.cjs.map +1 -1
- package/dist/chat/StatewireApprovalCard.d.ts +8 -1
- package/dist/chat/StatewireApprovalCard.js +7 -3
- package/dist/chat/StatewireApprovalCard.js.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.cjs.map +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.d.ts +6 -5
- package/dist/chat/super-grouping/CollapsibleGroup.js +1 -1
- package/dist/chat/super-grouping/CollapsibleGroup.js.map +1 -1
- package/dist/chat/super-grouping/earlier-steps-button.cjs +5 -6
- package/dist/chat/super-grouping/earlier-steps-button.cjs.map +1 -1
- package/dist/chat/super-grouping/earlier-steps-button.d.ts +6 -6
- package/dist/chat/super-grouping/earlier-steps-button.js +5 -6
- package/dist/chat/super-grouping/earlier-steps-button.js.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.cjs +1 -0
- package/dist/chat/super-grouping/toolkit-map.cjs.map +1 -1
- package/dist/chat/super-grouping/toolkit-map.js +1 -0
- package/dist/chat/super-grouping/toolkit-map.js.map +1 -1
- package/dist/chat/super-grouping/use-turn-steps.cjs +3 -3
- package/dist/chat/super-grouping/use-turn-steps.js +1 -1
- package/dist/chat-ui/index.cjs +4 -0
- package/dist/chat-ui/index.cjs.map +1 -1
- package/dist/chat-ui/index.d.ts +1 -0
- package/dist/chat-ui/index.js +5 -1
- package/dist/chat-ui/index.js.map +1 -1
- package/dist/chat-ui/messages/inbound-channel-message.cjs +219 -0
- package/dist/chat-ui/messages/inbound-channel-message.cjs.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-message.d.ts +60 -0
- package/dist/chat-ui/messages/inbound-channel-message.js +219 -0
- package/dist/chat-ui/messages/inbound-channel-message.js.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-reader.cjs +184 -0
- package/dist/chat-ui/messages/inbound-channel-reader.cjs.map +1 -0
- package/dist/chat-ui/messages/inbound-channel-reader.d.ts +65 -0
- package/dist/chat-ui/messages/inbound-channel-reader.js +184 -0
- package/dist/chat-ui/messages/inbound-channel-reader.js.map +1 -0
- package/dist/chat-ui/messages/message-components.cjs +18 -14
- package/dist/chat-ui/messages/message-components.cjs.map +1 -1
- package/dist/chat-ui/messages/message-components.d.ts +7 -0
- package/dist/chat-ui/messages/message-components.js +19 -15
- package/dist/chat-ui/messages/message-components.js.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.cjs +0 -4
- package/dist/chat-ui/thread/statewire-history-edge.cjs.map +1 -1
- package/dist/chat-ui/thread/statewire-history-edge.d.ts +5 -7
- package/dist/chat-ui/thread/statewire-history-edge.js +1 -5
- package/dist/chat-ui/thread/statewire-history-edge.js.map +1 -1
- package/dist/composer/sign-back-in-draft.cjs +62 -0
- package/dist/composer/sign-back-in-draft.cjs.map +1 -0
- package/dist/composer/sign-back-in-draft.d.ts +34 -0
- package/dist/composer/sign-back-in-draft.js +62 -0
- package/dist/composer/sign-back-in-draft.js.map +1 -0
- package/dist/diagnostics/athena-diagnostics.cjs.map +1 -1
- package/dist/diagnostics/athena-diagnostics.d.ts +6 -1
- package/dist/diagnostics/athena-diagnostics.js.map +1 -1
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/provider/AthenaProvider.cjs +60 -14
- package/dist/provider/AthenaProvider.cjs.map +1 -1
- package/dist/provider/AthenaProvider.js +60 -14
- package/dist/provider/AthenaProvider.js.map +1 -1
- package/dist/provider/statewire-window-context.cjs.map +1 -1
- package/dist/provider/statewire-window-context.d.ts +8 -8
- package/dist/provider/statewire-window-context.js.map +1 -1
- package/dist/react/package.json.cjs +1 -1
- package/dist/react/package.json.js +1 -1
- package/dist/runtime/auth.cjs +11 -0
- package/dist/runtime/auth.cjs.map +1 -1
- package/dist/runtime/auth.d.ts +18 -1
- package/dist/runtime/auth.js +11 -0
- package/dist/runtime/auth.js.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.cjs +0 -2
- package/dist/runtime/useAthenaStatewireRuntime.cjs.map +1 -1
- package/dist/runtime/useAthenaStatewireRuntime.d.ts +5 -5
- package/dist/runtime/useAthenaStatewireRuntime.js +0 -2
- package/dist/runtime/useAthenaStatewireRuntime.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/dist/agent-runtime/src/history-pages.cjs +0 -279
- package/dist/agent-runtime/src/history-pages.cjs.map +0 -1
- package/dist/agent-runtime/src/history-pages.d.ts +0 -252
- package/dist/agent-runtime/src/history-pages.js +0 -279
- package/dist/agent-runtime/src/history-pages.js.map +0 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sending a page through sign-in again when a session it held ends.
|
|
3
|
+
*
|
|
4
|
+
* Marathon serves a deployed app with a session of its own. That session ends
|
|
5
|
+
* when the person signs out of Athena anywhere (the web app, the desktop app or
|
|
6
|
+
* the phone), when its renewals reach their ceiling, or when the browser drops
|
|
7
|
+
* the cookie. An open tab learns it only on its next credential mint: a 401
|
|
8
|
+
* from `/api/sso/userinfo` or `/_athena/auth`. (A 403 there is Marathon
|
|
9
|
+
* refusing this viewer the app, `not_authorized` or `unknown_asset`: signing in
|
|
10
|
+
* again cannot change that, so it never starts a trip.) A page navigation would go
|
|
11
|
+
* through sign-in by itself, silently while the person is still signed in to
|
|
12
|
+
* Athena in this browser and to Athena's own sign-in page otherwise; a tab
|
|
13
|
+
* that makes no navigation used to stay on a dead "Sign in" screen instead.
|
|
14
|
+
* So the SDK makes that navigation: once, to the login URL with `return_to` set
|
|
15
|
+
* to the page's own URL, so the person comes back to the same place.
|
|
16
|
+
*
|
|
17
|
+
* At most one trip per tab every {@link SIGN_BACK_IN_GUARD_MS}, held in
|
|
18
|
+
* `sessionStorage`, so a trip that did not bring a session back never loops.
|
|
19
|
+
* A tab without `sessionStorage` never goes on its own. The redirect a
|
|
20
|
+
* provider without sign-in UI of its own makes ({@link redirectToSignIn})
|
|
21
|
+
* keeps the same guard.
|
|
22
|
+
*
|
|
23
|
+
* Marathon returns to `return_to` without its fragment, so a trip keeps the
|
|
24
|
+
* page's fragment (a hash-routed app's route) in the tab, and
|
|
25
|
+
* {@link restoreSignBackInHash} puts it back on the page that returns.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Dispatched on `window` right before the page leaves for sign-in, while the
|
|
29
|
+
* app is still mounted: `AthenaProvider` keeps the composer's draft then.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ATHENA_SIGN_BACK_IN_EVENT = "athena:sign-back-in";
|
|
32
|
+
/** How long after one trip through sign-in the tab does not start another on its own. */
|
|
33
|
+
export declare const SIGN_BACK_IN_GUARD_MS: number;
|
|
34
|
+
/** How long a trip through sign-in may take and still get the page's URL fragment back. */
|
|
35
|
+
export declare const SIGN_BACK_IN_RETURN_MS: number;
|
|
36
|
+
/** @internal Test seam: how the page is sent to a URL. */
|
|
37
|
+
export declare const signBackInNavigation: {
|
|
38
|
+
assign(url: string): void;
|
|
39
|
+
replace(url: string): void;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Whether this tab may go through sign-in on its own now: it has
|
|
43
|
+
* `sessionStorage`, and it has not gone in the last {@link SIGN_BACK_IN_GUARD_MS}.
|
|
44
|
+
*/
|
|
45
|
+
export declare function canSignBackIn({ now }?: {
|
|
46
|
+
now?: number;
|
|
47
|
+
}): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Whether the Marathon bridge's token is the credential the chat uses, so a
|
|
50
|
+
* session that ends under it should send the page through sign-in. Not when a
|
|
51
|
+
* sign-in provider is mounted above (`AthenaSSOProvider`, `AthenaAuthProvider`:
|
|
52
|
+
* it decides), and not when the host supplies a credential of its own (a
|
|
53
|
+
* `token`, even `null`, a `getToken` or an `apiKey`): that one keeps working
|
|
54
|
+
* when the bridge's session ends, and a trip would interrupt a working app.
|
|
55
|
+
*/
|
|
56
|
+
export declare function bridgeOwnsCredential({ signInProviderAbove, token, getToken, apiKey, }: {
|
|
57
|
+
signInProviderAbove: boolean;
|
|
58
|
+
/** The host's token override; `undefined` when it set none. */
|
|
59
|
+
token: string | null | undefined;
|
|
60
|
+
getToken: unknown;
|
|
61
|
+
apiKey: string | null | undefined;
|
|
62
|
+
}): boolean;
|
|
63
|
+
/**
|
|
64
|
+
* On the page a trip through sign-in returned to, puts back the URL fragment
|
|
65
|
+
* the page left with (a hash-routed app's route, `/#/chat/123`), which
|
|
66
|
+
* Marathon's return drops. Only on the same path, only when the page came back
|
|
67
|
+
* without a fragment of its own, only within {@link SIGN_BACK_IN_RETURN_MS} of
|
|
68
|
+
* leaving, and once: the kept fragment is deleted when read. A replace on the
|
|
69
|
+
* same document: hash routers hear it as a navigation, and Back does not
|
|
70
|
+
* return to the page without it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function restoreSignBackInHash({ now }?: {
|
|
73
|
+
now?: number;
|
|
74
|
+
}): void;
|
|
75
|
+
/**
|
|
76
|
+
* `loginUrl` with `return_to` set to `returnTo`, unless the login URL already
|
|
77
|
+
* names where to return. Relative login URLs resolve against the page.
|
|
78
|
+
*/
|
|
79
|
+
export declare function signInReturningTo({ loginUrl, returnTo, }: {
|
|
80
|
+
loginUrl: string;
|
|
81
|
+
returnTo: string;
|
|
82
|
+
}): string;
|
|
83
|
+
/** Reports that a session the page held ended, and whether the page went through sign-in for it. */
|
|
84
|
+
export declare function reportSessionEnded({ source, status, signBackIn, }: {
|
|
85
|
+
/** Which read found the session gone: the SSO provider's or the host bridge's. */
|
|
86
|
+
source: 'sso' | 'bridge';
|
|
87
|
+
/** The HTTP status that said so, when the caller knows it. */
|
|
88
|
+
status?: number;
|
|
89
|
+
signBackIn: boolean;
|
|
90
|
+
}): void;
|
|
91
|
+
/**
|
|
92
|
+
* Sends the page through sign-in once because a session it held ended:
|
|
93
|
+
* dispatches {@link ATHENA_SIGN_BACK_IN_EVENT}, then navigates to `loginUrl`
|
|
94
|
+
* with `return_to` set to the current URL, keeping the page's URL fragment for
|
|
95
|
+
* {@link restoreSignBackInHash}. Returns `false`, and goes nowhere, when
|
|
96
|
+
* {@link canSignBackIn} says no or the guard cannot be stored: the caller then
|
|
97
|
+
* shows its sign-in state. Reports the ending either way.
|
|
98
|
+
*/
|
|
99
|
+
export declare function signBackIn({ loginUrl, source, status, }: {
|
|
100
|
+
loginUrl: string;
|
|
101
|
+
source: 'sso' | 'bridge';
|
|
102
|
+
status?: number;
|
|
103
|
+
}): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Sends a page that has no session to show, and no sign-in UI of its own, to
|
|
106
|
+
* `loginUrl` with `return_to` set to the page. Keeps the guard
|
|
107
|
+
* {@link signBackIn} keeps: within {@link SIGN_BACK_IN_GUARD_MS} of a trip
|
|
108
|
+
* this tab started, the page stays where it is (the caller renders its
|
|
109
|
+
* signed-out state), so a trip that did not bring a session back never loops.
|
|
110
|
+
* A tab whose storage cannot say goes, as it did before the guard existed.
|
|
111
|
+
* Returns whether the page left.
|
|
112
|
+
*/
|
|
113
|
+
export declare function redirectToSignIn({ loginUrl, now, }: {
|
|
114
|
+
loginUrl: string;
|
|
115
|
+
/** Test seam. */
|
|
116
|
+
now?: number;
|
|
117
|
+
}): boolean;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { athenaDiagnostics } from "../diagnostics/athena-diagnostics.js";
|
|
2
|
+
const ATHENA_SIGN_BACK_IN_EVENT = "athena:sign-back-in";
|
|
3
|
+
const SIGN_BACK_IN_GUARD_MS = 2 * 60 * 1e3;
|
|
4
|
+
const SIGN_BACK_IN_RETURN_MS = 10 * 60 * 1e3;
|
|
5
|
+
const SIGN_BACK_IN_GUARD_KEY = "athena:sign-back-in-at";
|
|
6
|
+
const SIGN_BACK_IN_HASH_KEY = "athena:sign-back-in-hash";
|
|
7
|
+
const signBackInNavigation = {
|
|
8
|
+
assign(url) {
|
|
9
|
+
window.location.assign(url);
|
|
10
|
+
},
|
|
11
|
+
replace(url) {
|
|
12
|
+
window.location.replace(url);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
function lastTripAt() {
|
|
16
|
+
try {
|
|
17
|
+
const last = Number(window.sessionStorage.getItem(SIGN_BACK_IN_GUARD_KEY));
|
|
18
|
+
return Number.isFinite(last) && last > 0 ? last : null;
|
|
19
|
+
} catch {
|
|
20
|
+
return void 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function canSignBackIn({ now = Date.now() } = {}) {
|
|
24
|
+
if (typeof window === "undefined") return false;
|
|
25
|
+
const last = lastTripAt();
|
|
26
|
+
if (last === void 0) return false;
|
|
27
|
+
return last === null || now - last >= SIGN_BACK_IN_GUARD_MS;
|
|
28
|
+
}
|
|
29
|
+
function bridgeOwnsCredential({
|
|
30
|
+
signInProviderAbove,
|
|
31
|
+
token,
|
|
32
|
+
getToken,
|
|
33
|
+
apiKey
|
|
34
|
+
}) {
|
|
35
|
+
return !signInProviderAbove && token === void 0 && getToken === void 0 && !apiKey;
|
|
36
|
+
}
|
|
37
|
+
function isKeptHash(value) {
|
|
38
|
+
if (!value || typeof value !== "object") return false;
|
|
39
|
+
const candidate = value;
|
|
40
|
+
return typeof candidate.pathname === "string" && typeof candidate.hash === "string" && candidate.hash.startsWith("#") && candidate.hash.length > 1 && typeof candidate.at === "number" && Number.isFinite(candidate.at);
|
|
41
|
+
}
|
|
42
|
+
function keepHash(now) {
|
|
43
|
+
try {
|
|
44
|
+
const { hash, pathname } = window.location;
|
|
45
|
+
if (hash.length > 1) {
|
|
46
|
+
const kept = { pathname, hash, at: now };
|
|
47
|
+
window.sessionStorage.setItem(SIGN_BACK_IN_HASH_KEY, JSON.stringify(kept));
|
|
48
|
+
} else {
|
|
49
|
+
window.sessionStorage.removeItem(SIGN_BACK_IN_HASH_KEY);
|
|
50
|
+
}
|
|
51
|
+
} catch {
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function restoreSignBackInHash({ now = Date.now() } = {}) {
|
|
55
|
+
if (typeof window === "undefined") return;
|
|
56
|
+
let kept;
|
|
57
|
+
try {
|
|
58
|
+
const raw = window.sessionStorage.getItem(SIGN_BACK_IN_HASH_KEY);
|
|
59
|
+
if (raw === null) return;
|
|
60
|
+
window.sessionStorage.removeItem(SIGN_BACK_IN_HASH_KEY);
|
|
61
|
+
kept = JSON.parse(raw);
|
|
62
|
+
} catch {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (!isKeptHash(kept) || now - kept.at > SIGN_BACK_IN_RETURN_MS || kept.at > now + 6e4) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (window.location.hash || window.location.pathname !== kept.pathname) return;
|
|
69
|
+
const url = new URL(window.location.href);
|
|
70
|
+
url.hash = kept.hash;
|
|
71
|
+
signBackInNavigation.replace(url.href);
|
|
72
|
+
}
|
|
73
|
+
function signInReturningTo({
|
|
74
|
+
loginUrl,
|
|
75
|
+
returnTo
|
|
76
|
+
}) {
|
|
77
|
+
const url = new URL(loginUrl, returnTo);
|
|
78
|
+
if (!url.searchParams.has("return_to")) url.searchParams.set("return_to", returnTo);
|
|
79
|
+
return url.toString();
|
|
80
|
+
}
|
|
81
|
+
function reportSessionEnded({
|
|
82
|
+
source,
|
|
83
|
+
status,
|
|
84
|
+
signBackIn: signBackIn2
|
|
85
|
+
}) {
|
|
86
|
+
athenaDiagnostics.emit("auth.session.ended", {
|
|
87
|
+
level: signBackIn2 ? "info" : "warn",
|
|
88
|
+
data: { source, status: status ?? null, sign_back_in: signBackIn2 }
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function signBackIn({
|
|
92
|
+
loginUrl,
|
|
93
|
+
source,
|
|
94
|
+
status
|
|
95
|
+
}) {
|
|
96
|
+
let target = null;
|
|
97
|
+
if (canSignBackIn()) {
|
|
98
|
+
try {
|
|
99
|
+
target = signInReturningTo({ loginUrl, returnTo: window.location.href });
|
|
100
|
+
window.sessionStorage.setItem(SIGN_BACK_IN_GUARD_KEY, String(Date.now()));
|
|
101
|
+
} catch {
|
|
102
|
+
target = null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
reportSessionEnded({ source, status, signBackIn: target !== null });
|
|
106
|
+
if (target === null) return false;
|
|
107
|
+
keepHash(Date.now());
|
|
108
|
+
window.dispatchEvent(new Event(ATHENA_SIGN_BACK_IN_EVENT));
|
|
109
|
+
signBackInNavigation.assign(target);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
function redirectToSignIn({
|
|
113
|
+
loginUrl,
|
|
114
|
+
now = Date.now()
|
|
115
|
+
}) {
|
|
116
|
+
if (typeof window === "undefined") return false;
|
|
117
|
+
const last = lastTripAt();
|
|
118
|
+
if (typeof last === "number" && now - last < SIGN_BACK_IN_GUARD_MS) return false;
|
|
119
|
+
const target = signInReturningTo({ loginUrl, returnTo: window.location.href });
|
|
120
|
+
try {
|
|
121
|
+
window.sessionStorage.setItem(SIGN_BACK_IN_GUARD_KEY, String(now));
|
|
122
|
+
} catch {
|
|
123
|
+
}
|
|
124
|
+
keepHash(now);
|
|
125
|
+
signBackInNavigation.assign(target);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
ATHENA_SIGN_BACK_IN_EVENT,
|
|
130
|
+
SIGN_BACK_IN_GUARD_MS,
|
|
131
|
+
SIGN_BACK_IN_RETURN_MS,
|
|
132
|
+
bridgeOwnsCredential,
|
|
133
|
+
canSignBackIn,
|
|
134
|
+
redirectToSignIn,
|
|
135
|
+
reportSessionEnded,
|
|
136
|
+
restoreSignBackInHash,
|
|
137
|
+
signBackIn,
|
|
138
|
+
signBackInNavigation,
|
|
139
|
+
signInReturningTo
|
|
140
|
+
};
|
|
141
|
+
//# sourceMappingURL=sign-back-in.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sign-back-in.js","sources":["../../src/auth/sign-back-in.ts"],"sourcesContent":["import { athenaDiagnostics } from '../diagnostics/athena-diagnostics';\n\n/**\n * Sending a page through sign-in again when a session it held ends.\n *\n * Marathon serves a deployed app with a session of its own. That session ends\n * when the person signs out of Athena anywhere (the web app, the desktop app or\n * the phone), when its renewals reach their ceiling, or when the browser drops\n * the cookie. An open tab learns it only on its next credential mint: a 401\n * from `/api/sso/userinfo` or `/_athena/auth`. (A 403 there is Marathon\n * refusing this viewer the app, `not_authorized` or `unknown_asset`: signing in\n * again cannot change that, so it never starts a trip.) A page navigation would go\n * through sign-in by itself, silently while the person is still signed in to\n * Athena in this browser and to Athena's own sign-in page otherwise; a tab\n * that makes no navigation used to stay on a dead \"Sign in\" screen instead.\n * So the SDK makes that navigation: once, to the login URL with `return_to` set\n * to the page's own URL, so the person comes back to the same place.\n *\n * At most one trip per tab every {@link SIGN_BACK_IN_GUARD_MS}, held in\n * `sessionStorage`, so a trip that did not bring a session back never loops.\n * A tab without `sessionStorage` never goes on its own. The redirect a\n * provider without sign-in UI of its own makes ({@link redirectToSignIn})\n * keeps the same guard.\n *\n * Marathon returns to `return_to` without its fragment, so a trip keeps the\n * page's fragment (a hash-routed app's route) in the tab, and\n * {@link restoreSignBackInHash} puts it back on the page that returns.\n */\n\n/**\n * Dispatched on `window` right before the page leaves for sign-in, while the\n * app is still mounted: `AthenaProvider` keeps the composer's draft then.\n */\nexport const ATHENA_SIGN_BACK_IN_EVENT = 'athena:sign-back-in';\n\n/** How long after one trip through sign-in the tab does not start another on its own. */\nexport const SIGN_BACK_IN_GUARD_MS = 2 * 60 * 1000;\n\n/** How long a trip through sign-in may take and still get the page's URL fragment back. */\nexport const SIGN_BACK_IN_RETURN_MS = 10 * 60 * 1000;\n\nconst SIGN_BACK_IN_GUARD_KEY = 'athena:sign-back-in-at';\nconst SIGN_BACK_IN_HASH_KEY = 'athena:sign-back-in-hash';\n\n/** @internal Test seam: how the page is sent to a URL. */\nexport const signBackInNavigation = {\n assign(url: string): void {\n window.location.assign(url);\n },\n replace(url: string): void {\n window.location.replace(url);\n },\n};\n\n/**\n * When this tab last started a trip through sign-in: `null` when it has not,\n * `undefined` when its storage cannot say.\n */\nfunction lastTripAt(): number | null | undefined {\n try {\n const last = Number(window.sessionStorage.getItem(SIGN_BACK_IN_GUARD_KEY));\n return Number.isFinite(last) && last > 0 ? last : null;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Whether this tab may go through sign-in on its own now: it has\n * `sessionStorage`, and it has not gone in the last {@link SIGN_BACK_IN_GUARD_MS}.\n */\nexport function canSignBackIn({ now = Date.now() }: { now?: number } = {}): boolean {\n if (typeof window === 'undefined') return false;\n const last = lastTripAt();\n if (last === undefined) return false;\n return last === null || now - last >= SIGN_BACK_IN_GUARD_MS;\n}\n\n/**\n * Whether the Marathon bridge's token is the credential the chat uses, so a\n * session that ends under it should send the page through sign-in. Not when a\n * sign-in provider is mounted above (`AthenaSSOProvider`, `AthenaAuthProvider`:\n * it decides), and not when the host supplies a credential of its own (a\n * `token`, even `null`, a `getToken` or an `apiKey`): that one keeps working\n * when the bridge's session ends, and a trip would interrupt a working app.\n */\nexport function bridgeOwnsCredential({\n signInProviderAbove,\n token,\n getToken,\n apiKey,\n}: {\n signInProviderAbove: boolean;\n /** The host's token override; `undefined` when it set none. */\n token: string | null | undefined;\n getToken: unknown;\n apiKey: string | null | undefined;\n}): boolean {\n return !signInProviderAbove && token === undefined && getToken === undefined && !apiKey;\n}\n\ninterface KeptHash {\n pathname: string;\n hash: string;\n at: number;\n}\n\nfunction isKeptHash(value: unknown): value is KeptHash {\n if (!value || typeof value !== 'object') return false;\n const candidate = value as Partial<Record<keyof KeptHash, unknown>>;\n return (\n typeof candidate.pathname === 'string' &&\n typeof candidate.hash === 'string' &&\n candidate.hash.startsWith('#') &&\n candidate.hash.length > 1 &&\n typeof candidate.at === 'number' &&\n Number.isFinite(candidate.at)\n );\n}\n\n/** Keeps the page's URL fragment for the page a trip returns to, best effort. */\nfunction keepHash(now: number): void {\n try {\n const { hash, pathname } = window.location;\n if (hash.length > 1) {\n const kept: KeptHash = { pathname, hash, at: now };\n window.sessionStorage.setItem(SIGN_BACK_IN_HASH_KEY, JSON.stringify(kept));\n } else {\n window.sessionStorage.removeItem(SIGN_BACK_IN_HASH_KEY);\n }\n } catch {\n // The page comes back without its fragment, as it did before.\n }\n}\n\n/**\n * On the page a trip through sign-in returned to, puts back the URL fragment\n * the page left with (a hash-routed app's route, `/#/chat/123`), which\n * Marathon's return drops. Only on the same path, only when the page came back\n * without a fragment of its own, only within {@link SIGN_BACK_IN_RETURN_MS} of\n * leaving, and once: the kept fragment is deleted when read. A replace on the\n * same document: hash routers hear it as a navigation, and Back does not\n * return to the page without it.\n */\nexport function restoreSignBackInHash({ now = Date.now() }: { now?: number } = {}): void {\n if (typeof window === 'undefined') return;\n let kept: unknown;\n try {\n const raw = window.sessionStorage.getItem(SIGN_BACK_IN_HASH_KEY);\n if (raw === null) return;\n window.sessionStorage.removeItem(SIGN_BACK_IN_HASH_KEY);\n kept = JSON.parse(raw);\n } catch {\n return;\n }\n if (!isKeptHash(kept) || now - kept.at > SIGN_BACK_IN_RETURN_MS || kept.at > now + 60_000) {\n return;\n }\n if (window.location.hash || window.location.pathname !== kept.pathname) return;\n // Rebuilt from the page's own URL, so the result never leaves its origin.\n const url = new URL(window.location.href);\n url.hash = kept.hash;\n signBackInNavigation.replace(url.href);\n}\n\n/**\n * `loginUrl` with `return_to` set to `returnTo`, unless the login URL already\n * names where to return. Relative login URLs resolve against the page.\n */\nexport function signInReturningTo({\n loginUrl,\n returnTo,\n}: {\n loginUrl: string;\n returnTo: string;\n}): string {\n const url = new URL(loginUrl, returnTo);\n if (!url.searchParams.has('return_to')) url.searchParams.set('return_to', returnTo);\n return url.toString();\n}\n\n/** Reports that a session the page held ended, and whether the page went through sign-in for it. */\nexport function reportSessionEnded({\n source,\n status,\n signBackIn,\n}: {\n /** Which read found the session gone: the SSO provider's or the host bridge's. */\n source: 'sso' | 'bridge';\n /** The HTTP status that said so, when the caller knows it. */\n status?: number;\n signBackIn: boolean;\n}): void {\n athenaDiagnostics.emit('auth.session.ended', {\n level: signBackIn ? 'info' : 'warn',\n data: { source, status: status ?? null, sign_back_in: signBackIn },\n });\n}\n\n/**\n * Sends the page through sign-in once because a session it held ended:\n * dispatches {@link ATHENA_SIGN_BACK_IN_EVENT}, then navigates to `loginUrl`\n * with `return_to` set to the current URL, keeping the page's URL fragment for\n * {@link restoreSignBackInHash}. Returns `false`, and goes nowhere, when\n * {@link canSignBackIn} says no or the guard cannot be stored: the caller then\n * shows its sign-in state. Reports the ending either way.\n */\nexport function signBackIn({\n loginUrl,\n source,\n status,\n}: {\n loginUrl: string;\n source: 'sso' | 'bridge';\n status?: number;\n}): boolean {\n let target: string | null = null;\n if (canSignBackIn()) {\n try {\n target = signInReturningTo({ loginUrl, returnTo: window.location.href });\n window.sessionStorage.setItem(SIGN_BACK_IN_GUARD_KEY, String(Date.now()));\n } catch {\n target = null;\n }\n }\n reportSessionEnded({ source, status, signBackIn: target !== null });\n if (target === null) return false;\n keepHash(Date.now());\n window.dispatchEvent(new Event(ATHENA_SIGN_BACK_IN_EVENT));\n signBackInNavigation.assign(target);\n return true;\n}\n\n/**\n * Sends a page that has no session to show, and no sign-in UI of its own, to\n * `loginUrl` with `return_to` set to the page. Keeps the guard\n * {@link signBackIn} keeps: within {@link SIGN_BACK_IN_GUARD_MS} of a trip\n * this tab started, the page stays where it is (the caller renders its\n * signed-out state), so a trip that did not bring a session back never loops.\n * A tab whose storage cannot say goes, as it did before the guard existed.\n * Returns whether the page left.\n */\nexport function redirectToSignIn({\n loginUrl,\n now = Date.now(),\n}: {\n loginUrl: string;\n /** Test seam. */\n now?: number;\n}): boolean {\n if (typeof window === 'undefined') return false;\n const last = lastTripAt();\n if (typeof last === 'number' && now - last < SIGN_BACK_IN_GUARD_MS) return false;\n const target = signInReturningTo({ loginUrl, returnTo: window.location.href });\n try {\n window.sessionStorage.setItem(SIGN_BACK_IN_GUARD_KEY, String(now));\n } catch {\n // Unguarded, as before the guard existed.\n }\n keepHash(now);\n signBackInNavigation.assign(target);\n return true;\n}\n"],"names":["signBackIn"],"mappings":"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"}
|
package/dist/chat/AthenaChat.cjs
CHANGED
|
@@ -47,6 +47,7 @@ const useAccountWorkspaceId = require("../chat-ui/mentions/use-account-workspace
|
|
|
47
47
|
const answerLabel = require("../chat-ui/messages/answer-label.cjs");
|
|
48
48
|
const arrivalAnimation = require("../chat-ui/messages/arrival-animation.cjs");
|
|
49
49
|
const messageImages = require("../chat-ui/messages/message-images.cjs");
|
|
50
|
+
const inboundChannelMessage = require("../chat-ui/messages/inbound-channel-message.cjs");
|
|
50
51
|
const messageComponents = require("../chat-ui/messages/message-components.cjs");
|
|
51
52
|
const messageActionsConfig = require("../chat-ui/messages/message-actions-config.cjs");
|
|
52
53
|
const useAthenaRunLive = require("./use-athena-run-live.cjs");
|
|
@@ -1226,7 +1227,7 @@ const AthenaUserMessage = ({
|
|
|
1226
1227
|
);
|
|
1227
1228
|
const arrivedLive = arrivalAnimation.useMessageArrivalAnimation();
|
|
1228
1229
|
const partComponents = require$$0.useMemo(
|
|
1229
|
-
() => ({ Text: TextComponent, Image: messageImages.MessageImagePart }),
|
|
1230
|
+
() => ({ Text: inboundChannelMessage.withInboundMessageBody(TextComponent), Image: messageImages.MessageImagePart }),
|
|
1230
1231
|
[TextComponent]
|
|
1231
1232
|
);
|
|
1232
1233
|
if (isWake) {
|
|
@@ -1253,7 +1254,11 @@ const AthenaUserMessage = ({
|
|
|
1253
1254
|
children: [
|
|
1254
1255
|
/* @__PURE__ */ jsxRuntime.jsx(MessageDaySeparator, {}),
|
|
1255
1256
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 max-w-[75%] flex-col items-end gap-1", children: [
|
|
1256
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1257
|
+
/* @__PURE__ */ jsxRuntime.jsx(inboundChannelMessage.InboundChannelLine, {}),
|
|
1258
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aui-user-message-content wrap-break-word min-w-0 max-w-full rounded-2xl bg-muted/70 px-4 py-2.5 text-foreground", children: [
|
|
1259
|
+
/* @__PURE__ */ jsxRuntime.jsx(inboundChannelMessage.InboundChannelSubject, {}),
|
|
1260
|
+
/* @__PURE__ */ jsxRuntime.jsx(messageImages.MessageImageGallery, { children: /* @__PURE__ */ jsxRuntime.jsx(react.MessagePrimitive.Parts, { components: partComponents }) })
|
|
1261
|
+
] }),
|
|
1257
1262
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "aui-user-message-footer -mr-1 flex items-center gap-1", children: [
|
|
1258
1263
|
/* @__PURE__ */ jsxRuntime.jsx(MessageTimestamp, { className: "mr-1 group-hover/aui-user-message:opacity-100" }),
|
|
1259
1264
|
/* @__PURE__ */ jsxRuntime.jsx(messageComponents.BranchPicker, {}),
|