@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,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
FirstPartyMcpToolName,
|
|
3
|
+
CapabilityCatalogItem,
|
|
4
|
+
Session,
|
|
5
|
+
UpdateSessionToolPolicyRequest,
|
|
6
|
+
} from "@opengeni/sdk";
|
|
2
7
|
import type { OpenGeniBrowserClient as OpenGeniClient } from "@opengeni/sdk/browser";
|
|
3
8
|
|
|
4
9
|
/** Catalog tool discovery may be lazy; selecting the installed server does not
|
|
@@ -25,6 +30,16 @@ export async function attachSessionCapability(
|
|
|
25
30
|
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
26
31
|
const session = await client.getSession(workspaceId, sessionId);
|
|
27
32
|
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
33
|
+
const request = capabilityToolPolicyUpdate(session, item);
|
|
34
|
+
if (request) await client.updateSessionToolPolicy(workspaceId, sessionId, request);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function capabilityToolPolicyUpdate(
|
|
38
|
+
session: Session,
|
|
39
|
+
item: CapabilityCatalogItem,
|
|
40
|
+
): UpdateSessionToolPolicyRequest | null {
|
|
41
|
+
const tools = sessionCapabilityTools(item);
|
|
42
|
+
if (item.kind === "skill" || tools.length === 0) return null;
|
|
28
43
|
// Session.tools includes mandatory runtime servers, which must never be
|
|
29
44
|
// echoed into the human's explicit selection.
|
|
30
45
|
let existing = session.tools.filter(
|
|
@@ -42,10 +57,19 @@ export async function attachSessionCapability(
|
|
|
42
57
|
// automatic selection. Compare against the resolved defaults instead.
|
|
43
58
|
existing = policy.effectiveIds
|
|
44
59
|
.filter((id) => !policy.mandatoryIds.includes(id))
|
|
45
|
-
.map(
|
|
60
|
+
.map(
|
|
61
|
+
(id) =>
|
|
62
|
+
session.tools.find((tool) => tool.kind === "mcp" && tool.id === id) ?? {
|
|
63
|
+
kind: "mcp" as const,
|
|
64
|
+
id,
|
|
65
|
+
optional: true,
|
|
66
|
+
},
|
|
67
|
+
);
|
|
46
68
|
}
|
|
47
69
|
const additions = tools.filter(
|
|
48
|
-
(tool) =>
|
|
70
|
+
(tool) =>
|
|
71
|
+
!session.effectiveToolPolicy?.mandatoryIds.includes(tool.id) &&
|
|
72
|
+
!existing.some((current) => current.kind === tool.kind && current.id === tool.id),
|
|
49
73
|
);
|
|
50
74
|
const recommended = Array.isArray(item.metadata?.firstPartyMcpTools)
|
|
51
75
|
? (item.metadata.firstPartyMcpTools.filter(
|
|
@@ -53,13 +77,102 @@ export async function attachSessionCapability(
|
|
|
53
77
|
) as FirstPartyMcpToolName[])
|
|
54
78
|
: [];
|
|
55
79
|
const firstParty = [...new Set([...session.firstPartyMcpTools, ...recommended])];
|
|
56
|
-
if (additions.length === 0 && firstParty.length === session.firstPartyMcpTools.length)
|
|
57
|
-
|
|
80
|
+
if (additions.length === 0 && firstParty.length === session.firstPartyMcpTools.length)
|
|
81
|
+
return null;
|
|
82
|
+
return {
|
|
58
83
|
mode: "explicit",
|
|
59
84
|
tools: [...existing, ...additions],
|
|
60
85
|
firstPartyMcpTools: firstParty,
|
|
61
86
|
expectedVersion: session.toolPolicyVersion,
|
|
62
|
-
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Read-only review. Hosts must show ancestor changes and obtain a separate
|
|
91
|
+
* explicit click before applying. This plan is never authorization. */
|
|
92
|
+
export type SessionCapabilityAccessPlan = {
|
|
93
|
+
workspaceId: string;
|
|
94
|
+
sessionId: string;
|
|
95
|
+
capabilityId: string;
|
|
96
|
+
sessions: Array<{
|
|
97
|
+
id: string;
|
|
98
|
+
title: string | null;
|
|
99
|
+
parentSessionId: string | null;
|
|
100
|
+
toolPolicyVersion: number;
|
|
101
|
+
request: UpdateSessionToolPolicyRequest | null;
|
|
102
|
+
}>;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
export async function prepareSessionCapabilityAccess(
|
|
106
|
+
client: OpenGeniClient,
|
|
107
|
+
workspaceId: string,
|
|
108
|
+
sessionId: string,
|
|
109
|
+
item: CapabilityCatalogItem,
|
|
110
|
+
stillCurrent: () => boolean = () => true,
|
|
111
|
+
): Promise<SessionCapabilityAccessPlan> {
|
|
112
|
+
const sessions: SessionCapabilityAccessPlan["sessions"] = [];
|
|
113
|
+
if (item.kind === "skill" || sessionCapabilityTools(item).length === 0)
|
|
114
|
+
return { workspaceId, sessionId, capabilityId: item.id, sessions };
|
|
115
|
+
const visited = new Set<string>();
|
|
116
|
+
let id: string | null = sessionId;
|
|
117
|
+
while (id) {
|
|
118
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
119
|
+
if (visited.has(id)) throw new Error("The parent chat chain could not be verified.");
|
|
120
|
+
visited.add(id);
|
|
121
|
+
const session = await client.getSession(workspaceId, id);
|
|
122
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
123
|
+
const request = capabilityToolPolicyUpdate(session, item);
|
|
124
|
+
sessions.push({
|
|
125
|
+
id,
|
|
126
|
+
title: session.title ?? null,
|
|
127
|
+
parentSessionId: session.parentSessionId ?? null,
|
|
128
|
+
toolPolicyVersion: session.toolPolicyVersion,
|
|
129
|
+
request,
|
|
130
|
+
});
|
|
131
|
+
// An unchanged immediate parent already supplies this capability's ceiling.
|
|
132
|
+
// Do not read or broaden ancestors irrelevant to this addition.
|
|
133
|
+
if (!request) break;
|
|
134
|
+
id = session.parentSessionId ?? null;
|
|
135
|
+
}
|
|
136
|
+
return { workspaceId, sessionId, capabilityId: item.id, sessions: sessions.reverse() };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Preflight the entire review, then apply ordinary CAS-fenced API writes root
|
|
140
|
+
* first. A partial failure may leave approved ancestor changes committed; never
|
|
141
|
+
* claim success or roll back another person's edits. Prepare a new review. */
|
|
142
|
+
export async function applySessionCapabilityAccess(
|
|
143
|
+
client: OpenGeniClient,
|
|
144
|
+
plan: SessionCapabilityAccessPlan,
|
|
145
|
+
stillCurrent: () => boolean = () => true,
|
|
146
|
+
): Promise<void> {
|
|
147
|
+
for (const reviewed of plan.sessions) {
|
|
148
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
149
|
+
const current = await client.getSession(plan.workspaceId, reviewed.id);
|
|
150
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
151
|
+
if (
|
|
152
|
+
current.toolPolicyVersion !== reviewed.toolPolicyVersion ||
|
|
153
|
+
(current.parentSessionId ?? null) !== reviewed.parentSessionId
|
|
154
|
+
)
|
|
155
|
+
throw new Error("Chat access changed. Click Add tools to review the current choices again.");
|
|
156
|
+
}
|
|
157
|
+
for (const reviewed of plan.sessions) {
|
|
158
|
+
if (!stillCurrent()) throw new Error("Connection setup was interrupted.");
|
|
159
|
+
if (reviewed.request) {
|
|
160
|
+
const updated = await client.updateSessionToolPolicy(
|
|
161
|
+
plan.workspaceId,
|
|
162
|
+
reviewed.id,
|
|
163
|
+
reviewed.request,
|
|
164
|
+
);
|
|
165
|
+
if (
|
|
166
|
+
reviewed.request.mode === "explicit" &&
|
|
167
|
+
reviewed.request.firstPartyMcpTools.some(
|
|
168
|
+
(tool) => !updated.firstPartyMcpTools.includes(tool),
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
throw new Error(
|
|
172
|
+
"This chat's capabilities do not allow these tools. Review its capability settings before adding tools again.",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
63
176
|
}
|
|
64
177
|
|
|
65
178
|
/** Native OAuth callbacks have already saved the provider connection. Finish
|
package/src/timeline/index.ts
CHANGED
|
@@ -61,6 +61,16 @@ export type {
|
|
|
61
61
|
|
|
62
62
|
// renderer registry
|
|
63
63
|
export { createToolRegistry, rawTypeOf } from "./registry";
|
|
64
|
+
export {
|
|
65
|
+
createSessionRetainedScreenshotLoader,
|
|
66
|
+
createWorkspaceRetainedArtifactLoader,
|
|
67
|
+
createWorkspaceRetainedVideoLoader,
|
|
68
|
+
} from "./retained-loaders";
|
|
69
|
+
export type {
|
|
70
|
+
RetainedArtifactLoaderClient,
|
|
71
|
+
RetainedScreenshotLoaderClient,
|
|
72
|
+
RetainedVideoLoaderClient,
|
|
73
|
+
} from "./retained-loaders";
|
|
64
74
|
export type {
|
|
65
75
|
CreateToolRegistryOptions,
|
|
66
76
|
RetainedArtifactLoader,
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { OpenGeniClient, RetainedArtifactReference } from "@opengeni/sdk";
|
|
2
|
+
import type {
|
|
3
|
+
RetainedArtifactLoader,
|
|
4
|
+
RetainedScreenshotLoader,
|
|
5
|
+
VideoArtifactPlaybackLoader,
|
|
6
|
+
} from "./registry";
|
|
7
|
+
|
|
8
|
+
/** SDK methods the workspace retained-artifact loader needs. */
|
|
9
|
+
export type RetainedArtifactLoaderClient = Pick<
|
|
10
|
+
OpenGeniClient,
|
|
11
|
+
"createRetainedArtifactDownloadUrl" | "downloadRetainedArtifact"
|
|
12
|
+
>;
|
|
13
|
+
|
|
14
|
+
/** SDK method the session retained-screenshot loader needs. */
|
|
15
|
+
export type RetainedScreenshotLoaderClient = Pick<OpenGeniClient, "downloadRetainedScreenshot">;
|
|
16
|
+
|
|
17
|
+
/** SDK method the generated-video playback loader needs. */
|
|
18
|
+
export type RetainedVideoLoaderClient = Pick<OpenGeniClient, "createVideoArtifactPlaybackSource">;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Bind permanent retained-artifact retrieval (generated images, published
|
|
22
|
+
* files) to one authenticated workspace, for `MessageTimeline`'s
|
|
23
|
+
* `loadRetainedArtifact`. Images get a short-lived URL; files are assembled
|
|
24
|
+
* from verified byte ranges.
|
|
25
|
+
*/
|
|
26
|
+
export function createWorkspaceRetainedArtifactLoader(
|
|
27
|
+
client: RetainedArtifactLoaderClient,
|
|
28
|
+
workspaceId: string,
|
|
29
|
+
): RetainedArtifactLoader {
|
|
30
|
+
return async (artifact: RetainedArtifactReference, signal: AbortSignal) => {
|
|
31
|
+
if (artifact.kind === "file") {
|
|
32
|
+
const download = await client.downloadRetainedArtifact(workspaceId, artifact, { signal });
|
|
33
|
+
return download.bytes;
|
|
34
|
+
}
|
|
35
|
+
const download = await client.createRetainedArtifactDownloadUrl(workspaceId, artifact, {
|
|
36
|
+
signal,
|
|
37
|
+
});
|
|
38
|
+
return { url: download.url };
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Bind the authenticated screenshot downloader to one workspace and session,
|
|
44
|
+
* for `MessageTimeline`'s `loadRetainedScreenshot`. Fetched metadata must match
|
|
45
|
+
* the timeline receipt before bytes are handed to React; errors deliberately
|
|
46
|
+
* contain no storage locator or content.
|
|
47
|
+
*/
|
|
48
|
+
export function createSessionRetainedScreenshotLoader(
|
|
49
|
+
client: RetainedScreenshotLoaderClient,
|
|
50
|
+
workspaceId: string,
|
|
51
|
+
sessionId: string,
|
|
52
|
+
): RetainedScreenshotLoader {
|
|
53
|
+
return async (artifact: RetainedArtifactReference, signal: AbortSignal) => {
|
|
54
|
+
const download = await client.downloadRetainedScreenshot(
|
|
55
|
+
workspaceId,
|
|
56
|
+
sessionId,
|
|
57
|
+
artifact.artifactId,
|
|
58
|
+
{ signal },
|
|
59
|
+
);
|
|
60
|
+
if (!download.metadata.available || !download.bytes) return null;
|
|
61
|
+
if (
|
|
62
|
+
download.metadata.artifactId !== artifact.artifactId ||
|
|
63
|
+
download.metadata.kind !== artifact.kind ||
|
|
64
|
+
download.metadata.contentType !== artifact.contentType ||
|
|
65
|
+
download.metadata.originalBytes !== artifact.originalBytes ||
|
|
66
|
+
download.metadata.sha256 !== artifact.sha256 ||
|
|
67
|
+
download.metadata.dimensions?.width !== artifact.dimensions?.width ||
|
|
68
|
+
download.metadata.dimensions?.height !== artifact.dimensions?.height
|
|
69
|
+
) {
|
|
70
|
+
throw new Error("Retained screenshot receipt verification failed");
|
|
71
|
+
}
|
|
72
|
+
return download.bytes;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Bind expiring generated-video playback sources to one authenticated
|
|
78
|
+
* workspace, for `MessageTimeline`'s `loadVideoArtifactPlayback`.
|
|
79
|
+
*/
|
|
80
|
+
export function createWorkspaceRetainedVideoLoader(
|
|
81
|
+
client: RetainedVideoLoaderClient,
|
|
82
|
+
workspaceId: string,
|
|
83
|
+
): VideoArtifactPlaybackLoader {
|
|
84
|
+
return async (artifactId: string, signal: AbortSignal) =>
|
|
85
|
+
await client.createVideoArtifactPlaybackSource(workspaceId, artifactId, { signal });
|
|
86
|
+
}
|
|
@@ -30,7 +30,11 @@ export function toolMatchesLeaf(wireName: string, leaf: string): boolean {
|
|
|
30
30
|
*/
|
|
31
31
|
export function toolDisplayName(name: string, display?: ToolDisplayMetadata): string {
|
|
32
32
|
if (!display && /^[a-f0-9]{64}$/.test(name)) return "Tool call";
|
|
33
|
-
|
|
33
|
+
// "batch modify messages" → "Modify messages": batching is transport, not the action.
|
|
34
|
+
const phrase = (display?.toolName ?? mcpToolLeaf(name))
|
|
35
|
+
.replace(/[_-]+/g, " ")
|
|
36
|
+
.trim()
|
|
37
|
+
.replace(/^batch\s+(?=\S)/i, "");
|
|
34
38
|
const title =
|
|
35
39
|
display?.title ?? (phrase ? phrase.charAt(0).toUpperCase() + phrase.slice(1) : name);
|
|
36
40
|
return display?.accountLabel ? `${title} — ${display.accountLabel}` : title;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { useHasToolReview,
|
|
1
|
+
import { useHasToolReview, useRecordedToolReview } from "../components/tool-review-history";
|
|
2
|
+
import { ToolActionReviewCard } from "../components/tool-action-review";
|
|
3
|
+
import type { ToolReviewStatus } from "@opengeni/sdk";
|
|
2
4
|
import { KnowledgeReceiptRow } from "./knowledge-receipt";
|
|
3
5
|
import { defaultUrlTransform } from "react-markdown";
|
|
4
6
|
import { isRetainedImageContentType, useRetainedImageObjectUrl } from "./retained-image";
|
|
@@ -588,7 +590,10 @@ function ComputerCallRenderer({ item, loadRetainedScreenshot }: ToolRendererProp
|
|
|
588
590
|
// every transport.
|
|
589
591
|
const functionAction: ComputerAction | undefined =
|
|
590
592
|
!raw.action && item.name.startsWith("computer_") && item.name !== "computer_call"
|
|
591
|
-
? {
|
|
593
|
+
? {
|
|
594
|
+
type: item.name.slice("computer_".length),
|
|
595
|
+
...asComputerArgs(item.arguments),
|
|
596
|
+
}
|
|
592
597
|
: undefined;
|
|
593
598
|
const action = raw.action ?? functionAction;
|
|
594
599
|
const actions = raw.actions ?? (action ? [action] : []);
|
|
@@ -2335,10 +2340,74 @@ function RunOnRenderer({ item }: ToolRendererProps) {
|
|
|
2335
2340
|
*/
|
|
2336
2341
|
function GenericRenderer({ item }: ToolRendererProps) {
|
|
2337
2342
|
const reviewed = useHasToolReview(item.callId);
|
|
2338
|
-
if (reviewed && item.callId) return <
|
|
2343
|
+
if (reviewed && item.callId) return <ReviewedGenericRenderer item={item} />;
|
|
2339
2344
|
return <UnreviewedGenericRenderer item={item} />;
|
|
2340
2345
|
}
|
|
2341
2346
|
|
|
2347
|
+
/** Settled review states earn one quiet gutter word; success and waiting stay chip-free. */
|
|
2348
|
+
const REVIEW_CHIP: Partial<Record<ToolReviewStatus, DisclosureChip>> = {
|
|
2349
|
+
rejected: { tone: "interrupted", text: "declined" },
|
|
2350
|
+
cancelled: { tone: "interrupted", text: "not run" },
|
|
2351
|
+
expired: { tone: "interrupted", text: "expired" },
|
|
2352
|
+
stale: { tone: "interrupted", text: "not run" },
|
|
2353
|
+
revoked: { tone: "interrupted", text: "not run" },
|
|
2354
|
+
blocked: { tone: "interrupted", text: "blocked" },
|
|
2355
|
+
unknown: { tone: "bad", text: "outcome unknown" },
|
|
2356
|
+
partial: { tone: "bad", text: "partly done" },
|
|
2357
|
+
failed: { tone: "bad", text: "failed" },
|
|
2358
|
+
};
|
|
2359
|
+
|
|
2360
|
+
/**
|
|
2361
|
+
* A call that went through approval keeps the ordinary row shape, titled with
|
|
2362
|
+
* what was approved. The expanded body is the saved review plus the result.
|
|
2363
|
+
*/
|
|
2364
|
+
function ReviewedGenericRenderer({ item }: ToolRendererProps) {
|
|
2365
|
+
const { review, onViewDetails } = useRecordedToolReview(item.callId);
|
|
2366
|
+
if (!review) return <UnreviewedGenericRenderer item={item} />;
|
|
2367
|
+
const icon = <GenericToolIcon name={item.name} />;
|
|
2368
|
+
const waiting = review.status === "pending";
|
|
2369
|
+
const running =
|
|
2370
|
+
!waiting &&
|
|
2371
|
+
(review.status === "executing" || review.status === "approved") &&
|
|
2372
|
+
item.status === "running";
|
|
2373
|
+
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
2374
|
+
const chip =
|
|
2375
|
+
REVIEW_CHIP[review.status] ??
|
|
2376
|
+
(isError && !running && !waiting ? ({ tone: "bad", text: "error" } as const) : undefined);
|
|
2377
|
+
return (
|
|
2378
|
+
<ActivityDisclosure
|
|
2379
|
+
icon={icon}
|
|
2380
|
+
iconTone={chip?.tone === "bad" ? "failed" : waiting ? "accent" : "muted"}
|
|
2381
|
+
title={review.title}
|
|
2382
|
+
running={running}
|
|
2383
|
+
chip={chip}
|
|
2384
|
+
preview={
|
|
2385
|
+
waiting ? (
|
|
2386
|
+
"Waiting for your approval"
|
|
2387
|
+
) : running ? (
|
|
2388
|
+
<RunningPreview>Running…</RunningPreview>
|
|
2389
|
+
) : (
|
|
2390
|
+
(review.accountLabel ?? undefined)
|
|
2391
|
+
)
|
|
2392
|
+
}
|
|
2393
|
+
>
|
|
2394
|
+
<div className="py-1" data-approval-id={item.callId} data-review-origin="history">
|
|
2395
|
+
<ToolActionReviewCard
|
|
2396
|
+
bare
|
|
2397
|
+
review={{ ...review, availableActions: [] }}
|
|
2398
|
+
onViewDetails={onViewDetails}
|
|
2399
|
+
/>
|
|
2400
|
+
</div>
|
|
2401
|
+
{outText ? <PayloadBlock label="Result" value={outText} failed={isError} /> : null}
|
|
2402
|
+
</ActivityDisclosure>
|
|
2403
|
+
);
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
/** The runtime's exact refusal for a tool whose effective permission is Block. */
|
|
2407
|
+
const BLOCKED_OUTPUT = /Connector action was not executed: blocked\b/;
|
|
2408
|
+
/** The runtime's refusal to guess whether an approved action ran. */
|
|
2409
|
+
const UNCERTAIN_OUTPUT = /Connector action outcome is uncertain\b/;
|
|
2410
|
+
|
|
2342
2411
|
function UnreviewedGenericRenderer({ item }: ToolRendererProps) {
|
|
2343
2412
|
const running = item.status === "running";
|
|
2344
2413
|
const args = parseToolArgs(item.arguments);
|
|
@@ -2363,6 +2432,42 @@ function UnreviewedGenericRenderer({ item }: ToolRendererProps) {
|
|
|
2363
2432
|
}
|
|
2364
2433
|
|
|
2365
2434
|
const { text: outText, isError } = unwrapMcpOutput(item.output);
|
|
2435
|
+
// A permission Block is a decision, not a tool failure: say so plainly instead
|
|
2436
|
+
// of the generic "error occurred, please try again" wrapper.
|
|
2437
|
+
if (isError && BLOCKED_OUTPUT.test(outText)) {
|
|
2438
|
+
return (
|
|
2439
|
+
<ActivityDisclosure
|
|
2440
|
+
icon={icon}
|
|
2441
|
+
iconTone="muted"
|
|
2442
|
+
title={display}
|
|
2443
|
+
chip={{ tone: "interrupted", text: "blocked" }}
|
|
2444
|
+
preview="Blocked by your permission settings"
|
|
2445
|
+
>
|
|
2446
|
+
<p className="m-0 py-1 text-og-sm text-og-fg-muted">
|
|
2447
|
+
Your permission settings block this action, so it did not run. You can change this in the
|
|
2448
|
+
integration's tool permissions.
|
|
2449
|
+
</p>
|
|
2450
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
2451
|
+
</ActivityDisclosure>
|
|
2452
|
+
);
|
|
2453
|
+
}
|
|
2454
|
+
// An interrupted approved action may have run. Never invite a blind retry.
|
|
2455
|
+
if (isError && UNCERTAIN_OUTPUT.test(outText)) {
|
|
2456
|
+
return (
|
|
2457
|
+
<ActivityDisclosure
|
|
2458
|
+
icon={icon}
|
|
2459
|
+
iconTone="failed"
|
|
2460
|
+
title={display}
|
|
2461
|
+
chip={{ tone: "bad", text: "outcome unknown" }}
|
|
2462
|
+
preview="It may have run"
|
|
2463
|
+
>
|
|
2464
|
+
<p className="m-0 py-1 text-og-sm text-og-fg-muted">
|
|
2465
|
+
This action may have run. Check the result in the connected app before trying again.
|
|
2466
|
+
</p>
|
|
2467
|
+
<PayloadBlock label="Arguments" value={args} />
|
|
2468
|
+
</ActivityDisclosure>
|
|
2469
|
+
);
|
|
2470
|
+
}
|
|
2366
2471
|
// Cancelled is NOT an error — a user-cancelled tool should not surface the red
|
|
2367
2472
|
// error chip even if the output payload carries an isError flag (the error may be
|
|
2368
2473
|
// a consequence of the cancellation, not the tool's own failure).
|
|
@@ -2543,25 +2648,41 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
2543
2648
|
{ match: "name", name: "apply_patch_call", render: ApplyPatchRenderer },
|
|
2544
2649
|
{ match: "name", name: "apply_patch", render: ApplyPatchRenderer },
|
|
2545
2650
|
{ match: "name", name: "computer_call", render: ComputerCallRenderer },
|
|
2546
|
-
{
|
|
2651
|
+
{
|
|
2652
|
+
match: "name",
|
|
2653
|
+
name: "browser_screenshot",
|
|
2654
|
+
render: BrowserScreenshotRenderer,
|
|
2655
|
+
},
|
|
2547
2656
|
{ match: "name", name: "browser_observe", render: BrowserScreenshotRenderer },
|
|
2548
2657
|
{ match: "name", name: "browser_act", render: BrowserScreenshotRenderer },
|
|
2549
2658
|
// Function-mode computer tools (codex / chat-wire transports).
|
|
2550
2659
|
{ match: "name", name: "computer_screenshot", render: ComputerCallRenderer },
|
|
2551
2660
|
{ match: "name", name: "computer_click", render: ComputerCallRenderer },
|
|
2552
|
-
{
|
|
2661
|
+
{
|
|
2662
|
+
match: "name",
|
|
2663
|
+
name: "computer_double_click",
|
|
2664
|
+
render: ComputerCallRenderer,
|
|
2665
|
+
},
|
|
2553
2666
|
{ match: "name", name: "computer_move", render: ComputerCallRenderer },
|
|
2554
2667
|
{ match: "name", name: "computer_scroll", render: ComputerCallRenderer },
|
|
2555
2668
|
{ match: "name", name: "computer_type", render: ComputerCallRenderer },
|
|
2556
2669
|
{ match: "name", name: "computer_keypress", render: ComputerCallRenderer },
|
|
2557
2670
|
{ match: "name", name: "computer_drag", render: ComputerCallRenderer },
|
|
2558
2671
|
{ match: "name", name: "web_search_call", render: WebSearchRenderer },
|
|
2559
|
-
{
|
|
2672
|
+
{
|
|
2673
|
+
match: "name",
|
|
2674
|
+
name: "image_generation_call",
|
|
2675
|
+
render: GeneratedImageRenderer,
|
|
2676
|
+
},
|
|
2560
2677
|
{ match: "name", name: "generate_image", render: GeneratedImageRenderer },
|
|
2561
2678
|
{ match: "name", name: "generate_video", render: GeneratedVideoRenderer },
|
|
2562
2679
|
{ match: "name", name: "tool_search", render: ToolSearchRenderer },
|
|
2563
2680
|
{ match: "name", name: "view_image", render: ViewImageRenderer },
|
|
2564
|
-
{
|
|
2681
|
+
{
|
|
2682
|
+
match: "name",
|
|
2683
|
+
name: "sandbox_file_publish",
|
|
2684
|
+
render: SandboxFilePublishRenderer,
|
|
2685
|
+
},
|
|
2565
2686
|
{
|
|
2566
2687
|
match: "name",
|
|
2567
2688
|
name: "artifacts_create",
|
|
@@ -2586,13 +2707,25 @@ const BASE_ENTRIES: ToolRegistryEntry[] = [
|
|
|
2586
2707
|
render: SiteArtifactRenderer,
|
|
2587
2708
|
matchPrefixedLeaf: false,
|
|
2588
2709
|
},
|
|
2589
|
-
{
|
|
2590
|
-
|
|
2710
|
+
{
|
|
2711
|
+
match: "name",
|
|
2712
|
+
name: "environment_set_variable",
|
|
2713
|
+
render: SecretSetRenderer,
|
|
2714
|
+
},
|
|
2715
|
+
{
|
|
2716
|
+
match: "name",
|
|
2717
|
+
name: "variable_set_set_variable",
|
|
2718
|
+
render: SecretSetRenderer,
|
|
2719
|
+
},
|
|
2591
2720
|
{ match: "name", name: "search_documents", render: DocsSearchRenderer },
|
|
2592
2721
|
{ match: "name", name: "knowledge_search", render: DocsSearchRenderer },
|
|
2593
2722
|
{ match: "name", name: "memory_propose", render: MemoryProposeRenderer },
|
|
2594
2723
|
{ match: "name", name: "set_session_title", render: SetSessionTitleRenderer },
|
|
2595
|
-
{
|
|
2724
|
+
{
|
|
2725
|
+
match: "name",
|
|
2726
|
+
name: "set_other_session_title",
|
|
2727
|
+
render: SetSessionTitleRenderer,
|
|
2728
|
+
},
|
|
2596
2729
|
];
|
|
2597
2730
|
|
|
2598
2731
|
/** The built-in tool renderer registry: every first-party tool plus a fallback. */
|
|
@@ -98,6 +98,28 @@ async function createUnderlyingOwnerLease(): Promise<UnderlyingOwnerLease> {
|
|
|
98
98
|
return { ownerId, release: () => undefined };
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Owner ids whose document still holds its Web Lock, or null when this browser
|
|
103
|
+
* cannot answer. A lock is released the moment its tab closes or crashes, so a
|
|
104
|
+
* recording whose owner is absent here is recoverable immediately instead of
|
|
105
|
+
* after the heartbeat stale window.
|
|
106
|
+
*/
|
|
107
|
+
export async function liveVoiceRecordingOwnerIds(): Promise<ReadonlySet<string> | null> {
|
|
108
|
+
if (!hasWebLocks() || typeof navigator.locks.query !== "function") return null;
|
|
109
|
+
try {
|
|
110
|
+
const snapshot = await navigator.locks.query();
|
|
111
|
+
const live = new Set<string>();
|
|
112
|
+
for (const lock of [...(snapshot.held ?? []), ...(snapshot.pending ?? [])]) {
|
|
113
|
+
if (lock.name?.startsWith(VOICE_RECORDING_OWNER_LOCK_PREFIX)) {
|
|
114
|
+
live.add(lock.name.slice(VOICE_RECORDING_OWNER_LOCK_PREFIX.length));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return live;
|
|
118
|
+
} catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
101
123
|
function hasWebLocks(): boolean {
|
|
102
124
|
return (
|
|
103
125
|
typeof navigator !== "undefined" &&
|