@opengeni/react 1.3.0 → 1.4.2
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 +29 -1
- package/dist/accounts.js +2 -0
- package/dist/accounts.js.map +1 -1
- package/dist/artifacts-document.js +5 -4
- package/dist/artifacts-presentation.js +5 -4
- package/dist/artifacts-spreadsheet.js +4 -3
- package/dist/artifacts.js +14 -13
- package/dist/artifacts.js.map +1 -1
- package/dist/browser-viewer-WGJ43YGI.js +17 -0
- package/dist/chat-interactive-block-ZFHOKTMS.js +12 -0
- package/dist/{chunk-WEHMPOXK.js → chunk-2DDJ6YH6.js} +3 -1
- package/dist/{chunk-WEHMPOXK.js.map → chunk-2DDJ6YH6.js.map} +1 -1
- package/dist/{chunk-C5X5AJXK.js → chunk-2HVLQ26H.js} +5 -4
- package/dist/{chunk-C5X5AJXK.js.map → chunk-2HVLQ26H.js.map} +1 -1
- package/dist/{chunk-A4UJB2MM.js → chunk-32CGLXW2.js} +4 -3
- package/dist/{chunk-A4UJB2MM.js.map → chunk-32CGLXW2.js.map} +1 -1
- package/dist/{chunk-5CVXZA3Y.js → chunk-45NUOSXQ.js} +3 -2
- package/dist/{chunk-5CVXZA3Y.js.map → chunk-45NUOSXQ.js.map} +1 -1
- package/dist/{chunk-WUISZ3WU.js → chunk-53KJBGPZ.js} +5 -4
- package/dist/{chunk-WUISZ3WU.js.map → chunk-53KJBGPZ.js.map} +1 -1
- package/dist/{chunk-TMYGL6X5.js → chunk-55DXD22O.js} +73 -7
- package/dist/chunk-55DXD22O.js.map +1 -0
- package/dist/{chunk-JT4JW7BC.js → chunk-5I4VRWBB.js} +3 -1
- package/dist/{chunk-JT4JW7BC.js.map → chunk-5I4VRWBB.js.map} +1 -1
- package/dist/{chunk-UK4TINKT.js → chunk-B27E3V37.js} +3 -1
- package/dist/{chunk-UK4TINKT.js.map → chunk-B27E3V37.js.map} +1 -1
- package/dist/{chunk-J6DBS4T3.js → chunk-BNKV5ZLF.js} +5 -4
- package/dist/{chunk-J6DBS4T3.js.map → chunk-BNKV5ZLF.js.map} +1 -1
- package/dist/{chunk-YM2DE7BQ.js → chunk-C5BF4L26.js} +13 -12
- package/dist/{chunk-YM2DE7BQ.js.map → chunk-C5BF4L26.js.map} +1 -1
- package/dist/{chunk-YBXQDQFX.js → chunk-C7CMD56T.js} +6 -5
- package/dist/{chunk-YBXQDQFX.js.map → chunk-C7CMD56T.js.map} +1 -1
- package/dist/{chunk-UGLEYEMP.js → chunk-CM7LVUKC.js} +3 -1
- package/dist/{chunk-UGLEYEMP.js.map → chunk-CM7LVUKC.js.map} +1 -1
- package/dist/{chunk-PGSX7UCP.js → chunk-CREMJMVN.js} +1502 -833
- package/dist/chunk-CREMJMVN.js.map +1 -0
- package/dist/{chunk-JVYQRRC4.js → chunk-CVYCLFMH.js} +3 -1
- package/dist/{chunk-JVYQRRC4.js.map → chunk-CVYCLFMH.js.map} +1 -1
- package/dist/{chunk-Y2PRMYLC.js → chunk-G3U2A6GS.js} +3 -2
- package/dist/{chunk-Y2PRMYLC.js.map → chunk-G3U2A6GS.js.map} +1 -1
- package/dist/{chunk-WWEUO2PL.js → chunk-G6YRCIYJ.js} +202 -14
- package/dist/chunk-G6YRCIYJ.js.map +1 -0
- package/dist/{chunk-B3V6TG77.js → chunk-GC3PH6N7.js} +3 -1
- package/dist/{chunk-B3V6TG77.js.map → chunk-GC3PH6N7.js.map} +1 -1
- package/dist/{chunk-FZCP527Y.js → chunk-HXYOSSQO.js} +3 -2
- package/dist/{chunk-FZCP527Y.js.map → chunk-HXYOSSQO.js.map} +1 -1
- package/dist/{chunk-BRM6FVZA.js → chunk-IEE7WCLN.js} +4 -3
- package/dist/{chunk-BRM6FVZA.js.map → chunk-IEE7WCLN.js.map} +1 -1
- package/dist/{chunk-DYWBUX5Q.js → chunk-IMSUVYQB.js} +6 -5
- package/dist/{chunk-DYWBUX5Q.js.map → chunk-IMSUVYQB.js.map} +1 -1
- package/dist/{chunk-CY7WK5FD.js → chunk-IUHHPMAX.js} +203 -46
- package/dist/chunk-IUHHPMAX.js.map +1 -0
- package/dist/{chunk-SGKD2OCN.js → chunk-IYQCWGU5.js} +6 -5
- package/dist/{chunk-SGKD2OCN.js.map → chunk-IYQCWGU5.js.map} +1 -1
- package/dist/{chunk-J2QWVSQY.js → chunk-JH7V627O.js} +8 -7
- package/dist/{chunk-J2QWVSQY.js.map → chunk-JH7V627O.js.map} +1 -1
- package/dist/{chunk-EESD3PY3.js → chunk-JHTIMBOL.js} +6 -5
- package/dist/{chunk-EESD3PY3.js.map → chunk-JHTIMBOL.js.map} +1 -1
- package/dist/{chunk-LNIRAOA3.js → chunk-L2J3PU7A.js} +7 -6
- package/dist/{chunk-LNIRAOA3.js.map → chunk-L2J3PU7A.js.map} +1 -1
- package/dist/{chunk-ZYRGEKWS.js → chunk-LJUMYUBO.js} +3 -1
- package/dist/{chunk-ZYRGEKWS.js.map → chunk-LJUMYUBO.js.map} +1 -1
- package/dist/{chunk-4RVYOY4O.js → chunk-LSW2OJVV.js} +4 -3
- package/dist/{chunk-4RVYOY4O.js.map → chunk-LSW2OJVV.js.map} +1 -1
- package/dist/{chunk-UTRTG6LH.js → chunk-LVS3MH5T.js} +3 -1
- package/dist/{chunk-UTRTG6LH.js.map → chunk-LVS3MH5T.js.map} +1 -1
- package/dist/chunk-M357MHH3.js +2 -0
- package/dist/{chunk-GYVRFZDY.js → chunk-MDIXFKA3.js} +9 -8
- package/dist/{chunk-GYVRFZDY.js.map → chunk-MDIXFKA3.js.map} +1 -1
- package/dist/{chunk-WRHIZYZF.js → chunk-MEQM4LI3.js} +6 -5
- package/dist/{chunk-WRHIZYZF.js.map → chunk-MEQM4LI3.js.map} +1 -1
- package/dist/chunk-NX4ADH73.js +14 -0
- package/dist/chunk-NX4ADH73.js.map +1 -0
- package/dist/{chunk-XCHWDJ22.js → chunk-OGTTUYNT.js} +3 -1
- package/dist/{chunk-XCHWDJ22.js.map → chunk-OGTTUYNT.js.map} +1 -1
- package/dist/{chunk-GIMRELBW.js → chunk-OYQQJL3J.js} +3 -1
- package/dist/{chunk-GIMRELBW.js.map → chunk-OYQQJL3J.js.map} +1 -1
- package/dist/{chunk-RSI7XYXL.js → chunk-PBCZL4JP.js} +6 -5
- package/dist/{chunk-RSI7XYXL.js.map → chunk-PBCZL4JP.js.map} +1 -1
- package/dist/{chunk-I7IWRQLN.js → chunk-PHBFJCEU.js} +17 -11
- package/dist/chunk-PHBFJCEU.js.map +1 -0
- package/dist/{chunk-3CTRFMAK.js → chunk-PKCAYHDC.js} +5 -4
- package/dist/{chunk-3CTRFMAK.js.map → chunk-PKCAYHDC.js.map} +1 -1
- package/dist/{chunk-K3HQAKQX.js → chunk-PMV53IMM.js} +3 -1
- package/dist/{chunk-K3HQAKQX.js.map → chunk-PMV53IMM.js.map} +1 -1
- package/dist/{chunk-YQEAA6LT.js → chunk-QHUE7E4N.js} +3 -2
- package/dist/{chunk-YQEAA6LT.js.map → chunk-QHUE7E4N.js.map} +1 -1
- package/dist/{chunk-RC6BZ44Q.js → chunk-QMMCJGFI.js} +4 -3
- package/dist/{chunk-RC6BZ44Q.js.map → chunk-QMMCJGFI.js.map} +1 -1
- package/dist/{chunk-YV4L3ILF.js → chunk-QV3DJVRZ.js} +3 -1
- package/dist/{chunk-YV4L3ILF.js.map → chunk-QV3DJVRZ.js.map} +1 -1
- package/dist/{chunk-447JLCBU.js → chunk-S2QH6EZ5.js} +3 -1
- package/dist/{chunk-447JLCBU.js.map → chunk-S2QH6EZ5.js.map} +1 -1
- package/dist/{chunk-PNY3JKBJ.js → chunk-SFH43JT2.js} +5 -4
- package/dist/{chunk-PNY3JKBJ.js.map → chunk-SFH43JT2.js.map} +1 -1
- package/dist/{chunk-ERGYJIBB.js → chunk-T46QZH3E.js} +4 -2
- package/dist/{chunk-ERGYJIBB.js.map → chunk-T46QZH3E.js.map} +1 -1
- package/dist/{chunk-5J4FOW6Q.js → chunk-TNEZNMSN.js} +3 -1
- package/dist/{chunk-5J4FOW6Q.js.map → chunk-TNEZNMSN.js.map} +1 -1
- package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
- package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
- package/dist/{chunk-742CZ3L3.js → chunk-UDPH5SLV.js} +19 -202
- package/dist/chunk-UDPH5SLV.js.map +1 -0
- package/dist/{chunk-HDJWDK7W.js → chunk-VCSERYXH.js} +10 -9
- package/dist/{chunk-HDJWDK7W.js.map → chunk-VCSERYXH.js.map} +1 -1
- package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
- package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
- package/dist/{chunk-5F4WR7X7.js → chunk-XS67YG5R.js} +3 -1
- package/dist/{chunk-5F4WR7X7.js.map → chunk-XS67YG5R.js.map} +1 -1
- package/dist/{chunk-TKOTKRWY.js → chunk-XTD7TZHW.js} +3 -1
- package/dist/{chunk-TKOTKRWY.js.map → chunk-XTD7TZHW.js.map} +1 -1
- package/dist/{chunk-A2PI6UMY.js → chunk-YSRBFJMQ.js} +3 -1
- package/dist/{chunk-A2PI6UMY.js.map → chunk-YSRBFJMQ.js.map} +1 -1
- package/dist/chunk-YVFRBXST.js +1420 -0
- package/dist/chunk-YVFRBXST.js.map +1 -0
- package/dist/{chunk-SCOTPE2I.js → chunk-YYLBXXTE.js} +3 -1
- package/dist/{chunk-SCOTPE2I.js.map → chunk-YYLBXXTE.js.map} +1 -1
- package/dist/{chunk-RHREZ7BO.js → chunk-ZUWF4NF7.js} +3 -1
- package/dist/{chunk-RHREZ7BO.js.map → chunk-ZUWF4NF7.js.map} +1 -1
- package/dist/clipboard.js +3 -2
- package/dist/components/composer-transcription-control.d.ts +7 -0
- package/dist/components/open-geni-chat.d.ts +6 -2
- package/dist/components/session-conversation.d.ts +31 -19
- package/dist/components/session-list.d.ts +4 -1
- package/dist/components/tool-action-review.d.ts +29 -2
- package/dist/components/tool-review-history.d.ts +8 -4
- package/dist/composer-voice-events.d.ts +8 -0
- package/dist/composer.js +15 -13
- package/dist/computer-viewer-46UIN7F3.js +20 -0
- package/dist/connect.js +15 -12
- package/dist/connect.js.map +1 -1
- package/dist/desktop.js +6 -5
- package/dist/desktop.js.map +1 -1
- package/dist/diffs.js +2 -1
- package/dist/diffs.js.map +1 -1
- package/dist/editor.js +4 -3
- package/dist/editor.js.map +1 -1
- package/dist/embedded-voice-NIL4RDX3.js +69 -0
- package/dist/embedded-voice-NIL4RDX3.js.map +1 -0
- package/dist/{fleet-decision-row-YZ3ZKWMM.js → fleet-decision-row-SI63O5V4.js} +3 -1
- package/dist/{fleet-decision-row-YZ3ZKWMM.js.map → fleet-decision-row-SI63O5V4.js.map} +1 -1
- package/dist/hooks/use-client-config-flags.d.ts +43 -0
- package/dist/hooks/use-realtime-voice-models.d.ts +9 -0
- package/dist/hooks/use-voice-input.d.ts +8 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.js +60 -48
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +14 -13
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-YTOBHCWR.js → latest-question-2POFPYNB.js} +5 -4
- package/dist/{latest-question-YTOBHCWR.js.map → latest-question-2POFPYNB.js.map} +1 -1
- package/dist/machines.js +8 -7
- package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-DQ4DRNVT.js} +3 -1
- package/dist/{markdown-table-layout-QXSLK4BY.js.map → markdown-table-layout-DQ4DRNVT.js.map} +1 -1
- package/dist/model-policy-picker-menu-WRNSRFQC.js +15 -0
- package/dist/model-policy.js +2 -1
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
- package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
- package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
- package/dist/{platform-activity-row-627BFZ46.js → platform-activity-row-PJJVATTZ.js} +3 -1
- package/dist/{platform-activity-row-627BFZ46.js.map → platform-activity-row-PJJVATTZ.js.map} +1 -1
- package/dist/{queue-surface-implementation-ZHFI5JBP.js → queue-surface-implementation-67FS7FUI.js} +10 -9
- package/dist/{queue-surface-implementation-ZHFI5JBP.js.map → queue-surface-implementation-67FS7FUI.js.map} +1 -1
- package/dist/realtime/embedded-voice.d.ts +20 -0
- package/dist/realtime/realtime-control.d.ts +2 -0
- package/dist/realtime.js +22 -1332
- package/dist/realtime.js.map +1 -1
- package/dist/sandbox-files-34CJJNOY.js +21 -0
- package/dist/session-capability-policy.d.ts +20 -1
- package/dist/session-ui.js +32 -30
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +12 -11
- package/dist/sites.js +2 -1
- package/dist/terminal.js +6 -5
- package/dist/terminal.js.map +1 -1
- package/dist/timeline/index.d.ts +2 -0
- package/dist/timeline/retained-loaders.d.ts +27 -0
- package/dist/{timeline-annotation-markers-GNDHTGBD.js → timeline-annotation-markers-U3G66KUD.js} +5 -4
- package/dist/{timeline-annotation-markers-GNDHTGBD.js.map → timeline-annotation-markers-U3G66KUD.js.map} +1 -1
- package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-BI7QTPTZ.js} +4 -3
- package/dist/{timeline-annotation-selection-DMLMM2TX.js.map → timeline-annotation-selection-BI7QTPTZ.js.map} +1 -1
- package/dist/{timeline-annotations-dialog-ELRDBHTS.js → timeline-annotations-dialog-FOMHSDTE.js} +7 -6
- package/dist/{timeline-annotations-dialog-ELRDBHTS.js.map → timeline-annotations-dialog-FOMHSDTE.js.map} +1 -1
- package/dist/usage.js +6 -5
- package/dist/usage.js.map +1 -1
- package/dist/voice-recording-owner.d.ts +7 -0
- package/package.json +3 -3
- package/src/components/approval-surface.tsx +190 -79
- package/src/components/artifacts/session-artifact-viewer.tsx +4 -2
- package/src/components/composer-transcription-control.tsx +121 -25
- package/src/components/composer.tsx +1 -0
- package/src/components/markdown.tsx +7 -2
- package/src/components/message-timeline.tsx +10 -1
- package/src/components/open-geni-chat.tsx +53 -13
- package/src/components/session-conversation.tsx +273 -63
- package/src/components/session-list.tsx +9 -2
- package/src/components/session-proxy-scope.tsx +26 -5
- package/src/components/tool-action-review.tsx +502 -239
- package/src/components/tool-review-history.tsx +18 -43
- package/src/composer-voice-events.ts +12 -0
- package/src/connection-discovery.tsx +4 -1
- package/src/hooks/use-client-config-flags.ts +110 -0
- package/src/hooks/use-goal.ts +12 -1
- package/src/hooks/use-realtime-voice-models.ts +67 -0
- package/src/hooks/use-session-events.ts +19 -16
- package/src/hooks/use-transcription.ts +6 -0
- package/src/hooks/use-voice-input.ts +115 -18
- package/src/index.ts +9 -0
- package/src/realtime/embedded-voice.tsx +81 -0
- package/src/realtime/realtime-control.tsx +109 -17
- package/src/session-capability-policy.ts +119 -6
- package/src/timeline/index.ts +10 -0
- package/src/timeline/retained-loaders.ts +86 -0
- package/src/timeline/tool-display-name.ts +5 -1
- package/src/timeline/tool-renderers.tsx +143 -10
- package/src/voice-recording-owner.ts +22 -0
- package/styles/compiled.css +135 -37
- package/styles/index.css +49 -0
- package/styles/responsive.css +61 -4
- package/styles/session-capability.css +1 -1
- package/styles/tokens.css +1 -1
- package/dist/browser-viewer-SHYKQRNX.js +0 -16
- package/dist/chat-interactive-block-UGBTNB3L.js +0 -11
- package/dist/chunk-742CZ3L3.js.map +0 -1
- package/dist/chunk-CY7WK5FD.js.map +0 -1
- package/dist/chunk-I7IWRQLN.js.map +0 -1
- package/dist/chunk-IWIDV7SB.js +0 -1
- package/dist/chunk-PGSX7UCP.js.map +0 -1
- package/dist/chunk-TMYGL6X5.js.map +0 -1
- package/dist/chunk-WWEUO2PL.js.map +0 -1
- package/dist/computer-viewer-6SRGWRUX.js +0 -19
- package/dist/model-policy-picker-menu-DXHUL73H.js +0 -14
- package/dist/sandbox-files-GORZOUUJ.js +0 -20
- /package/dist/{browser-viewer-SHYKQRNX.js.map → browser-viewer-WGJ43YGI.js.map} +0 -0
- /package/dist/{chat-interactive-block-UGBTNB3L.js.map → chat-interactive-block-ZFHOKTMS.js.map} +0 -0
- /package/dist/{chunk-IWIDV7SB.js.map → chunk-M357MHH3.js.map} +0 -0
- /package/dist/{computer-viewer-6SRGWRUX.js.map → computer-viewer-46UIN7F3.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-DXHUL73H.js.map → model-policy-picker-menu-WRNSRFQC.js.map} +0 -0
- /package/dist/{sandbox-files-GORZOUUJ.js.map → sandbox-files-34CJJNOY.js.map} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from "react";
|
|
2
2
|
import type { SessionEvent, ToolActionReview } from "@opengeni/sdk";
|
|
3
3
|
import { approvalsFromRequiresAction } from "../approvals";
|
|
4
|
-
import { ToolActionReviewCard } from "./tool-action-review";
|
|
5
4
|
|
|
6
5
|
type HistoryContext = {
|
|
7
6
|
approvalIds: ReadonlySet<string>;
|
|
@@ -55,59 +54,35 @@ export function useHasToolReview(approvalId: string | null): boolean {
|
|
|
55
54
|
return Boolean(approvalId && history?.approvalIds.has(approvalId));
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
/**
|
|
59
|
-
|
|
57
|
+
/**
|
|
58
|
+
* The saved review for one recorded call, or null while it loads or when it is
|
|
59
|
+
* unavailable (callers then keep their ordinary tool row).
|
|
60
|
+
*/
|
|
61
|
+
export function useRecordedToolReview(approvalId: string | null): {
|
|
62
|
+
review: ToolActionReview | null;
|
|
63
|
+
onViewDetails: ((path: string) => void) | undefined;
|
|
64
|
+
} {
|
|
60
65
|
const history = useContext(History);
|
|
61
66
|
const [review, setReview] = useState<ToolActionReview | null>(null);
|
|
62
|
-
const [failed, setFailed] = useState(false);
|
|
63
|
-
const [retry, setRetry] = useState(0);
|
|
64
67
|
const load = history?.load;
|
|
65
68
|
const revision = history?.revision;
|
|
66
69
|
useEffect(() => {
|
|
67
|
-
if (!load) return;
|
|
70
|
+
if (!load || !approvalId) return;
|
|
68
71
|
let active = true;
|
|
69
|
-
setFailed(false);
|
|
70
72
|
void load(approvalId).then(
|
|
71
73
|
(value) => {
|
|
72
|
-
if (active)
|
|
73
|
-
if (value.id === approvalId) setReview(value);
|
|
74
|
-
else setFailed(true);
|
|
75
|
-
}
|
|
76
|
-
},
|
|
77
|
-
() => {
|
|
78
|
-
if (active) setFailed(true);
|
|
74
|
+
if (active && value.id === approvalId) setReview(value);
|
|
79
75
|
},
|
|
76
|
+
() => undefined,
|
|
80
77
|
);
|
|
81
78
|
return () => {
|
|
82
79
|
active = false;
|
|
83
80
|
};
|
|
84
|
-
}, [approvalId, load, revision
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
type="button"
|
|
92
|
-
className="min-h-11 underline"
|
|
93
|
-
onClick={() => setRetry((value) => value + 1)}
|
|
94
|
-
>
|
|
95
|
-
Try again
|
|
96
|
-
</button>
|
|
97
|
-
</p>
|
|
98
|
-
);
|
|
99
|
-
if (!review)
|
|
100
|
-
return (
|
|
101
|
-
<p role="status" className="text-og-sm text-og-fg-muted">
|
|
102
|
-
Loading reviewed action…
|
|
103
|
-
</p>
|
|
104
|
-
);
|
|
105
|
-
return (
|
|
106
|
-
<div data-approval-id={approvalId} data-review-origin="history">
|
|
107
|
-
<ToolActionReviewCard
|
|
108
|
-
review={{ ...review, availableActions: [] }}
|
|
109
|
-
onViewDetails={(path) => history.onViewDetails(review, path)}
|
|
110
|
-
/>
|
|
111
|
-
</div>
|
|
112
|
-
);
|
|
81
|
+
}, [approvalId, load, revision]);
|
|
82
|
+
const onViewDetails = history?.onViewDetails;
|
|
83
|
+
return {
|
|
84
|
+
review: review && review.id === approvalId ? review : null,
|
|
85
|
+
onViewDetails:
|
|
86
|
+
review && onViewDetails ? (path: string) => onViewDetails(review, path) : undefined,
|
|
87
|
+
};
|
|
113
88
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal DOM signal between the composer's two voice controls: dictation
|
|
3
|
+
* starting supersedes a stale live-voice start failure in the same composer.
|
|
4
|
+
* Not part of the public package surface.
|
|
5
|
+
*/
|
|
6
|
+
export const COMPOSER_VOICE_INPUT_START_EVENT = "opengeni:composer-voice-input-start";
|
|
7
|
+
|
|
8
|
+
/** Closest composer root shared by both voice controls (falls back to the document). */
|
|
9
|
+
export function composerVoiceEventScope(element: Element | null): EventTarget | null {
|
|
10
|
+
if (!element) return null;
|
|
11
|
+
return element.closest(".og-composer") ?? element.ownerDocument;
|
|
12
|
+
}
|
|
@@ -92,7 +92,10 @@ function ScopedDiscovery({
|
|
|
92
92
|
(item) =>
|
|
93
93
|
item.kind === "mcp" &&
|
|
94
94
|
item.authKind === "oauth2" &&
|
|
95
|
-
Boolean(item.mcpUrl ?? item.endpointUrl)
|
|
95
|
+
Boolean(item.mcpUrl ?? item.endpointUrl) &&
|
|
96
|
+
// The provider refuses self-registration and this deployment
|
|
97
|
+
// has no operator client for it: Connect cannot succeed.
|
|
98
|
+
(item.enabled || item.runtime?.operatorOAuthClient?.configured !== false),
|
|
96
99
|
),
|
|
97
100
|
);
|
|
98
101
|
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import type { ClientVoiceInputConfig, ReasoningEffort } from "@opengeni/sdk";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
/** The client-config fields the stock conversation and chat read. */
|
|
5
|
+
export type ClientConfigFlagsSource = {
|
|
6
|
+
getClientConfig: () => Promise<{
|
|
7
|
+
fileUploads?: { enabled?: boolean };
|
|
8
|
+
modelSelection?: boolean | undefined;
|
|
9
|
+
sandboxFiles?: boolean | undefined;
|
|
10
|
+
defaultReasoningEffort?: ReasoningEffort | undefined;
|
|
11
|
+
artifacts?: unknown;
|
|
12
|
+
sessionCreation?: boolean | undefined;
|
|
13
|
+
archive?: boolean | undefined;
|
|
14
|
+
voiceInput?: ClientVoiceInputConfig | undefined;
|
|
15
|
+
realtimeVoice?: boolean | undefined;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ClientConfigFlags = {
|
|
20
|
+
/** Whether the config has loaded. Proxy capability flags stay `true` until it says otherwise. */
|
|
21
|
+
loaded: boolean;
|
|
22
|
+
uploads: boolean;
|
|
23
|
+
modelSelection: boolean;
|
|
24
|
+
sandboxFiles: boolean;
|
|
25
|
+
defaultReasoningEffort: ReasoningEffort | null;
|
|
26
|
+
/**
|
|
27
|
+
* Inline Site previews and the artifact viewer. `false` only when a session
|
|
28
|
+
* proxy says it does not serve them; native deployments omit the flag.
|
|
29
|
+
*/
|
|
30
|
+
artifacts: boolean;
|
|
31
|
+
/** Browser chat creation. `false` only when a session proxy has no `createSession` hook. */
|
|
32
|
+
sessionCreation: boolean;
|
|
33
|
+
/** Chat archive/restore. `false` only when a session proxy disables it. */
|
|
34
|
+
archive: boolean;
|
|
35
|
+
/** Native voice input when the deployment can transcribe, else null. */
|
|
36
|
+
voiceInput: ClientVoiceInputConfig | null;
|
|
37
|
+
/**
|
|
38
|
+
* Live voice. `false` until the config loads and when a session proxy turns
|
|
39
|
+
* it off; availability otherwise comes from the realtime model catalog.
|
|
40
|
+
*/
|
|
41
|
+
realtimeVoice: boolean;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const INITIAL_FLAGS: ClientConfigFlags = {
|
|
45
|
+
loaded: false,
|
|
46
|
+
uploads: false,
|
|
47
|
+
modelSelection: false,
|
|
48
|
+
sandboxFiles: false,
|
|
49
|
+
defaultReasoningEffort: null,
|
|
50
|
+
artifacts: true,
|
|
51
|
+
sessionCreation: true,
|
|
52
|
+
archive: true,
|
|
53
|
+
voiceInput: null,
|
|
54
|
+
realtimeVoice: false,
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
type ClientConfigFlagsInput = Awaited<ReturnType<ClientConfigFlagsSource["getClientConfig"]>>;
|
|
58
|
+
|
|
59
|
+
// One read per client: the chat, its list, the new-chat composer and the
|
|
60
|
+
// conversation all ask, and behind a session proxy each read is a round trip.
|
|
61
|
+
const configReads = new WeakMap<object, Promise<ClientConfigFlagsInput>>();
|
|
62
|
+
|
|
63
|
+
function readClientConfig(client: ClientConfigFlagsSource): Promise<ClientConfigFlagsInput> {
|
|
64
|
+
let read = configReads.get(client);
|
|
65
|
+
if (!read) {
|
|
66
|
+
read = client.getClientConfig();
|
|
67
|
+
configReads.set(client, read);
|
|
68
|
+
// A failed read is retried by the next mount rather than cached.
|
|
69
|
+
read.catch(() => {
|
|
70
|
+
if (configReads.get(client) === read) configReads.delete(client);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
return read;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Deployment and session-proxy capabilities from the client config. */
|
|
77
|
+
export function useClientConfigFlags(
|
|
78
|
+
client: Partial<ClientConfigFlagsSource> | ClientConfigFlagsSource,
|
|
79
|
+
): ClientConfigFlags {
|
|
80
|
+
const [flags, setFlags] = useState<ClientConfigFlags>(INITIAL_FLAGS);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
// Custom clients without a config read keep the defaults.
|
|
83
|
+
if (typeof client.getClientConfig !== "function") return;
|
|
84
|
+
let live = true;
|
|
85
|
+
readClientConfig(client as ClientConfigFlagsSource).then(
|
|
86
|
+
(config) => {
|
|
87
|
+
if (live) {
|
|
88
|
+
setFlags({
|
|
89
|
+
loaded: true,
|
|
90
|
+
uploads: config.fileUploads?.enabled === true,
|
|
91
|
+
// Only an explicit offer shows end users the picker.
|
|
92
|
+
modelSelection: config.modelSelection === true,
|
|
93
|
+
sandboxFiles: config.sandboxFiles !== false,
|
|
94
|
+
defaultReasoningEffort: config.defaultReasoningEffort ?? null,
|
|
95
|
+
artifacts: config.artifacts !== false,
|
|
96
|
+
sessionCreation: config.sessionCreation !== false,
|
|
97
|
+
archive: config.archive !== false,
|
|
98
|
+
voiceInput: config.voiceInput?.available === true ? config.voiceInput : null,
|
|
99
|
+
realtimeVoice: config.realtimeVoice !== false,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
() => undefined,
|
|
104
|
+
);
|
|
105
|
+
return () => {
|
|
106
|
+
live = false;
|
|
107
|
+
};
|
|
108
|
+
}, [client]);
|
|
109
|
+
return flags;
|
|
110
|
+
}
|
package/src/hooks/use-goal.ts
CHANGED
|
@@ -84,6 +84,9 @@ export function useGoal(
|
|
|
84
84
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
85
85
|
const generation = useRef(0);
|
|
86
86
|
const targetKeyRef = useRef<string | null>(null);
|
|
87
|
+
// True after the server answered 404. Only a goal event can create a goal,
|
|
88
|
+
// so turn/session traffic must not refetch (and 404) while none exists.
|
|
89
|
+
const goalAbsentRef = useRef(false);
|
|
87
90
|
const loadAbort = useRef<AbortController | null>(null);
|
|
88
91
|
|
|
89
92
|
const retireLoads = useCallback(() => {
|
|
@@ -105,6 +108,7 @@ export function useGoal(
|
|
|
105
108
|
signal: controller.signal,
|
|
106
109
|
});
|
|
107
110
|
if (ticket === generation.current) {
|
|
111
|
+
goalAbsentRef.current = false;
|
|
108
112
|
setGoal(fetched);
|
|
109
113
|
setError(null);
|
|
110
114
|
setLoading(false);
|
|
@@ -115,6 +119,7 @@ export function useGoal(
|
|
|
115
119
|
}
|
|
116
120
|
if (cause instanceof OpenGeniApiError && cause.status === 404) {
|
|
117
121
|
// No goal is a normal state, not an error.
|
|
122
|
+
goalAbsentRef.current = true;
|
|
118
123
|
setGoal(null);
|
|
119
124
|
setError(null);
|
|
120
125
|
} else {
|
|
@@ -130,6 +135,7 @@ export function useGoal(
|
|
|
130
135
|
const targetKey = `${workspaceId} ${sessionId ?? ""}`;
|
|
131
136
|
if (targetKeyRef.current !== targetKey) {
|
|
132
137
|
targetKeyRef.current = targetKey;
|
|
138
|
+
goalAbsentRef.current = false;
|
|
133
139
|
setGoal(null);
|
|
134
140
|
setError(null);
|
|
135
141
|
}
|
|
@@ -181,7 +187,12 @@ export function useGoal(
|
|
|
181
187
|
]);
|
|
182
188
|
|
|
183
189
|
const scheduleRefresh = useDebouncedCallback(() => void load());
|
|
184
|
-
|
|
190
|
+
const isRefreshEvent = useCallback(
|
|
191
|
+
(event: SessionEvent) =>
|
|
192
|
+
goalAbsentRef.current ? isGoalEvent(event) : isGoalRefreshEvent(event),
|
|
193
|
+
[],
|
|
194
|
+
);
|
|
195
|
+
useSessionEventTrigger(client, workspaceId, sessionId, isRefreshEvent, scheduleRefresh, {
|
|
185
196
|
enabled,
|
|
186
197
|
...(sharedEvents !== undefined ? { events: sharedEvents } : {}),
|
|
187
198
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { WorkspaceRealtimeModelCatalogResponse } from "@opengeni/sdk";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
type CatalogClient = {
|
|
5
|
+
getWorkspaceRealtimeModelCatalog?: (
|
|
6
|
+
workspaceId: string,
|
|
7
|
+
options?: { signal?: AbortSignal | undefined },
|
|
8
|
+
) => Promise<WorkspaceRealtimeModelCatalogResponse>;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
type AvailableVoiceModels = WorkspaceRealtimeModelCatalogResponse["models"];
|
|
12
|
+
|
|
13
|
+
const CACHE_TTL_MS = 60_000;
|
|
14
|
+
const cache = new WeakMap<
|
|
15
|
+
object,
|
|
16
|
+
Map<string, { models: Promise<AvailableVoiceModels>; expiresAt: number }>
|
|
17
|
+
>();
|
|
18
|
+
|
|
19
|
+
function load(client: CatalogClient, workspaceId: string): Promise<AvailableVoiceModels> {
|
|
20
|
+
let byWorkspace = cache.get(client);
|
|
21
|
+
if (!byWorkspace) {
|
|
22
|
+
byWorkspace = new Map();
|
|
23
|
+
cache.set(client, byWorkspace);
|
|
24
|
+
}
|
|
25
|
+
const hit = byWorkspace.get(workspaceId);
|
|
26
|
+
if (hit && hit.expiresAt > Date.now()) return hit.models;
|
|
27
|
+
// A throwing structural client (one without voice) just means no voice.
|
|
28
|
+
const models = Promise.resolve()
|
|
29
|
+
.then(() => client.getWorkspaceRealtimeModelCatalog!(workspaceId))
|
|
30
|
+
.then((catalog) => catalog.models.filter((model) => model.available));
|
|
31
|
+
const entry = { models, expiresAt: Date.now() + CACHE_TTL_MS };
|
|
32
|
+
byWorkspace.set(workspaceId, entry);
|
|
33
|
+
models.catch(() => {
|
|
34
|
+
if (byWorkspace.get(workspaceId) === entry) byWorkspace.delete(workspaceId);
|
|
35
|
+
});
|
|
36
|
+
return models;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Voice models this user can start right now in the workspace, or an empty
|
|
41
|
+
* list while unknown, disabled, unsupported (older API or proxy), or failed.
|
|
42
|
+
* Availability is advisory; Opengeni still admits and meters every call.
|
|
43
|
+
*/
|
|
44
|
+
export function useRealtimeVoiceModels(
|
|
45
|
+
client: unknown,
|
|
46
|
+
workspaceId: string,
|
|
47
|
+
enabled: boolean,
|
|
48
|
+
): AvailableVoiceModels {
|
|
49
|
+
const [models, setModels] = useState<AvailableVoiceModels>([]);
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
setModels([]);
|
|
52
|
+
const catalogClient = client as CatalogClient;
|
|
53
|
+
if (!enabled || !workspaceId) return;
|
|
54
|
+
if (typeof catalogClient.getWorkspaceRealtimeModelCatalog !== "function") return;
|
|
55
|
+
let live = true;
|
|
56
|
+
load(catalogClient, workspaceId).then(
|
|
57
|
+
(available) => {
|
|
58
|
+
if (live) setModels(available);
|
|
59
|
+
},
|
|
60
|
+
() => undefined,
|
|
61
|
+
);
|
|
62
|
+
return () => {
|
|
63
|
+
live = false;
|
|
64
|
+
};
|
|
65
|
+
}, [client, workspaceId, enabled]);
|
|
66
|
+
return models;
|
|
67
|
+
}
|
|
@@ -225,8 +225,8 @@ export function useSessionEvents(
|
|
|
225
225
|
// new stream identity cannot expose the previous session's event log.
|
|
226
226
|
const [stateStreamKey, setStateStreamKey] = useState(streamKey);
|
|
227
227
|
|
|
228
|
-
//
|
|
229
|
-
//
|
|
228
|
+
// Navigation belongs to the session/client lifetime, not the transport: an
|
|
229
|
+
// SSE reconnect must never cancel an in-flight history page.
|
|
230
230
|
useEffect(() => {
|
|
231
231
|
navigationGenerationRef.current += 1;
|
|
232
232
|
setNewerError(null);
|
|
@@ -621,9 +621,6 @@ export function useSessionEvents(
|
|
|
621
621
|
}
|
|
622
622
|
const current = eventWindowRef.current;
|
|
623
623
|
assertPrependOrder(current.events, window.events);
|
|
624
|
-
// Freeze the live iterator before replacing its in-memory window. Rows
|
|
625
|
-
// pending in the aborted iterator were never cursor-committed and will
|
|
626
|
-
// be replayed from the retained high-water mark below.
|
|
627
624
|
const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
|
|
628
625
|
direction: "oldest",
|
|
629
626
|
});
|
|
@@ -633,7 +630,6 @@ export function useSessionEvents(
|
|
|
633
630
|
markTailPreserved();
|
|
634
631
|
return false;
|
|
635
632
|
}
|
|
636
|
-
streamAbortRef.current?.abort();
|
|
637
633
|
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
638
634
|
const retained = {
|
|
639
635
|
...next,
|
|
@@ -645,21 +641,28 @@ export function useSessionEvents(
|
|
|
645
641
|
}
|
|
646
642
|
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
647
643
|
const previousNewest = maxResumeSequenceOrNull(current.events);
|
|
644
|
+
const evictedLiveTail =
|
|
645
|
+
previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
646
|
+
if (evictedLiveTail && viewModeRef.current === "live") {
|
|
647
|
+
// The prepend leaves the live tip. Freeze the iterator before the
|
|
648
|
+
// window is replaced so a late flush cannot append live rows onto
|
|
649
|
+
// (and newest-bound away) the history the reader just requested.
|
|
650
|
+
streamAbortRef.current?.abort();
|
|
651
|
+
}
|
|
648
652
|
eventWindowRef.current = retained;
|
|
649
653
|
markCommitted();
|
|
650
654
|
oldestSequenceRef.current = retainedOldest;
|
|
651
655
|
newestSequenceRef.current = retainedNewest;
|
|
652
|
-
|
|
656
|
+
if (evictedLiveTail) {
|
|
657
|
+
streamResumeSequenceRef.current = maxResumeSequence(retained.events);
|
|
658
|
+
}
|
|
653
659
|
// Oldest-directed eviction can discard newer in-memory rows. That fact
|
|
654
660
|
// keeps windowTruncated true, but it does not imply older durable rows
|
|
655
661
|
// exist; only the backward DB page can answer hasOlder truthfully.
|
|
656
662
|
const olderStillAvailable = window.hasOlder;
|
|
657
663
|
hasOlderRef.current = olderStillAvailable;
|
|
658
|
-
const evictedLiveTail =
|
|
659
|
-
previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
660
664
|
let newer: boolean | null = null;
|
|
661
665
|
let enterHistory = false;
|
|
662
|
-
let reconnectLive = false;
|
|
663
666
|
if (viewModeRef.current === "history" || evictedLiveTail) {
|
|
664
667
|
const highWater = lastSequenceRef.current;
|
|
665
668
|
newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
|
|
@@ -672,11 +675,13 @@ export function useSessionEvents(
|
|
|
672
675
|
viewModeRef.current = "history";
|
|
673
676
|
enterHistory = true;
|
|
674
677
|
}
|
|
675
|
-
} else {
|
|
676
|
-
// The retained merge is still one contiguous live suffix. Restart
|
|
677
|
-
// SSE from its cursor so events queued during the fetch are replayed.
|
|
678
|
-
reconnectLive = true;
|
|
679
678
|
}
|
|
679
|
+
// Otherwise the merge is still one contiguous live suffix and the open
|
|
680
|
+
// SSE iterator keeps appending to it: every flush reads
|
|
681
|
+
// eventWindowRef, so rows delivered during or after this fetch land
|
|
682
|
+
// once, in order, on the prepended window. Reopening the stream here
|
|
683
|
+
// would flash a connecting state and re-run session reconciliation on
|
|
684
|
+
// every scroll-up for no data benefit.
|
|
680
685
|
loadingOlderRef.current = false;
|
|
681
686
|
startTransition(() => {
|
|
682
687
|
if (status !== undefined) {
|
|
@@ -689,8 +694,6 @@ export function useSessionEvents(
|
|
|
689
694
|
}
|
|
690
695
|
if (enterHistory) {
|
|
691
696
|
setViewMode("history");
|
|
692
|
-
} else if (reconnectLive) {
|
|
693
|
-
setStreamEpoch((epoch) => epoch + 1);
|
|
694
697
|
}
|
|
695
698
|
setLoadingOlder(false);
|
|
696
699
|
});
|
|
@@ -611,6 +611,12 @@ function classifyStartError(error: unknown): TranscriptionErrorCode {
|
|
|
611
611
|
|
|
612
612
|
export function normalizeTranscriptionErrorCode(code: unknown): TranscriptionErrorCode {
|
|
613
613
|
switch (code) {
|
|
614
|
+
case "insufficient_credits":
|
|
615
|
+
case "allowance_exhausted":
|
|
616
|
+
case "monthly_model_cost_limit":
|
|
617
|
+
case "unavailable":
|
|
618
|
+
case "too_large":
|
|
619
|
+
case "invalid_audio":
|
|
614
620
|
case "permission_denied":
|
|
615
621
|
case "not_supported":
|
|
616
622
|
case "network":
|