@datalayer/agent-runtimes 1.3.114 → 1.5.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/dist-embed/chunks/ChatView-CaBuPTyG.js +32 -0
- package/dist-embed/chunks/{DeckComponent-CuRWiARr.js → DeckComponent-cRJJMGgt.js} +1 -1
- package/dist-embed/chunks/{DocumentView-BYU2HREd.js → DocumentView-CiqN9ze6.js} +1 -1
- package/dist-embed/chunks/{EphemeralDocument-BjFSuQKH.js → EphemeralDocument-BjJfn7J-.js} +1 -1
- package/dist-embed/chunks/{ExcalidrawComponent-CFLnMPnf.js → ExcalidrawComponent-BtNXWUN8.js} +1 -1
- package/dist-embed/chunks/{ExcalidrawModal-CB9d5Vpk.js → ExcalidrawModal-C6SEKFlD.js} +1 -1
- package/dist-embed/chunks/{JupyterLexicalExtension-CYnZrelh.js → JupyterLexicalExtension-BuRLImy5.js} +5 -5
- package/dist-embed/chunks/{NotebookToolSurfaces-D62q-lbb.js → NotebookToolSurfaces-ktcGRm7d.js} +1 -1
- package/dist-embed/chunks/{Output-2KxoKu5F.js → Output-BscOPLqE.js} +10 -10
- package/dist-embed/chunks/{YouTubePlugin-CjODyxZ2.js → YouTubePlugin-xBjNr2Iu.js} +1 -1
- package/dist-embed/chunks/{ar-SA-G6X2FPQ2-C-zAYgA-.js → ar-SA-G6X2FPQ2-BB82iKLz.js} +1 -1
- package/dist-embed/chunks/{az-AZ-76LH7QW2-BHq5js6U.js → az-AZ-76LH7QW2-DsMRKXlw.js} +1 -1
- package/dist-embed/chunks/{bg-BG-XCXSNQG7-BTcNNcTc.js → bg-BG-XCXSNQG7-43Y1kPxa.js} +1 -1
- package/dist-embed/chunks/{bn-BD-2XOGV67Q-DXRk27u1.js → bn-BD-2XOGV67Q-CLqPJBll.js} +1 -1
- package/dist-embed/chunks/{ca-ES-6MX7JW3Y-D-0J9rqp.js → ca-ES-6MX7JW3Y-CDFGSEh3.js} +1 -1
- package/dist-embed/chunks/{cs-CZ-2BRQDIVT-CgN3I-57.js → cs-CZ-2BRQDIVT-ByNB8eZd.js} +1 -1
- package/dist-embed/chunks/{da-DK-5WZEPLOC-CHQloSoX.js → da-DK-5WZEPLOC-B7MrVwy8.js} +1 -1
- package/dist-embed/chunks/{de-DE-XR44H4JA-C-Hq84pe.js → de-DE-XR44H4JA-CN1cdk94.js} +1 -1
- package/dist-embed/chunks/{el-GR-BZB4AONW-MTzGcSsP.js → el-GR-BZB4AONW-DnaGhWD1.js} +1 -1
- package/dist-embed/chunks/{es-ES-U4NZUMDT-Thj-4Rwn.js → es-ES-U4NZUMDT-DE1vZzDE.js} +1 -1
- package/dist-embed/chunks/{eu-ES-A7QVB2H4-BEEyNQo-.js → eu-ES-A7QVB2H4-DmHB0zpm.js} +1 -1
- package/dist-embed/chunks/{fa-IR-HGAKTJCU-CtUGmNBZ.js → fa-IR-HGAKTJCU-CNzE8tJj.js} +1 -1
- package/dist-embed/chunks/{fi-FI-Z5N7JZ37-6GMgpkSu.js → fi-FI-Z5N7JZ37-Ca2vTFQC.js} +1 -1
- package/dist-embed/chunks/{fr-FR-RHASNOE6-BakNCbxj.js → fr-FR-RHASNOE6-B0yWFX3_.js} +1 -1
- package/dist-embed/chunks/{gl-ES-HMX3MZ6V-Cwkb9OPk.js → gl-ES-HMX3MZ6V-2_3FMVJq.js} +1 -1
- package/dist-embed/chunks/{he-IL-6SHJWFNN-BaMmvCkr.js → he-IL-6SHJWFNN-BW1lby00.js} +1 -1
- package/dist-embed/chunks/{hi-IN-IWLTKZ5I-1aWewhep.js → hi-IN-IWLTKZ5I-DTixfSwB.js} +1 -1
- package/dist-embed/chunks/{hu-HU-A5ZG7DT2-BIHI23BQ.js → hu-HU-A5ZG7DT2-DOfYwLWP.js} +1 -1
- package/dist-embed/chunks/{id-ID-SAP4L64H-CYpgmx8X.js → id-ID-SAP4L64H-Cwtdtkvg.js} +1 -1
- package/dist-embed/chunks/{index-BhSMzuKq.js → index--qMT5k1q.js} +1 -1
- package/dist-embed/chunks/{index-b1yzCiTq.js → index-B-UP9DlM.js} +1 -1
- package/dist-embed/chunks/{index-DMKsCn-y.js → index-BAwsCLGT.js} +1 -1
- package/dist-embed/chunks/{index-BUhqZVtg.js → index-BITOMOWN.js} +1 -1
- package/dist-embed/chunks/{index-1wTrAXI1.js → index-B_9UD0bt.js} +2 -2
- package/dist-embed/chunks/{index-DtlUttQj.js → index-BgFbP5P0.js} +1 -1
- package/dist-embed/chunks/{index-CC-Ht79i.js → index-CMUTS_sA.js} +1 -1
- package/dist-embed/chunks/{index-B7nmHsEo.js → index-Cgf7tlW3.js} +1 -1
- package/dist-embed/chunks/{index-CsZ2HycN.js → index-CrNfUmIU.js} +1 -1
- package/dist-embed/chunks/{index-DIOcQUrz.js → index-CzuLxFBK.js} +1 -1
- package/dist-embed/chunks/{index-EmZm7_bH.js → index-DEn5R18M.js} +1 -1
- package/dist-embed/chunks/{index-DeVZ-nmF.js → index-DFEBhySA.js} +1 -1
- package/dist-embed/chunks/{index-CMYyiALV.js → index-DYkwi0aX.js} +1 -1
- package/dist-embed/chunks/{index-Fzz2KjFM.js → index-DvpdAlVH.js} +1 -1
- package/dist-embed/chunks/{index-W7TVoWP5.js → index-Dyh5xopp.js} +1 -1
- package/dist-embed/chunks/{index-en9Yf1Yz.js → index-Jt4bWLoz.js} +1 -1
- package/dist-embed/chunks/{index-BbucLMre.js → index-S-sjhoD3.js} +1 -1
- package/dist-embed/chunks/{index-D18sf241.js → index-TRLGqZaF.js} +1 -1
- package/dist-embed/chunks/{index-ZZnjmFwm.js → index-YcqCoWJl.js} +1 -1
- package/dist-embed/chunks/{index-BFLtEBuP.js → index-sQy5cmWo.js} +1 -1
- package/dist-embed/chunks/{it-IT-JPQ66NNP-CLcqplyX.js → it-IT-JPQ66NNP-C3xZ61-6.js} +1 -1
- package/dist-embed/chunks/{ja-JP-DBVTYXUO-u8Qe6tR7.js → ja-JP-DBVTYXUO-CeTmglEb.js} +1 -1
- package/dist-embed/chunks/{kaa-6HZHGXH3-BG2kx50I.js → kaa-6HZHGXH3-D6Jr6B6Y.js} +1 -1
- package/dist-embed/chunks/{kab-KAB-ZGHBKWFO-CSYUpRxd.js → kab-KAB-ZGHBKWFO-pEXBqeuG.js} +1 -1
- package/dist-embed/chunks/{kk-KZ-P5N5QNE5-CpBMgHAz.js → kk-KZ-P5N5QNE5-BRJunmHm.js} +1 -1
- package/dist-embed/chunks/{km-KH-HSX4SM5Z-CoMUUQMb.js → km-KH-HSX4SM5Z--GJgT9rN.js} +1 -1
- package/dist-embed/chunks/{ko-KR-MTYHY66A-DwxY8CGI.js → ko-KR-MTYHY66A-Bjnez4qR.js} +1 -1
- package/dist-embed/chunks/{ku-TR-6OUDTVRD-UoNYUfm0.js → ku-TR-6OUDTVRD-DgOdYJrS.js} +1 -1
- package/dist-embed/chunks/{lt-LT-XHIRWOB4-Dr4uXaEj.js → lt-LT-XHIRWOB4-BMwhrtjM.js} +1 -1
- package/dist-embed/chunks/{lv-LV-5QDEKY6T-CNC56J-_.js → lv-LV-5QDEKY6T-0pxW7YE0.js} +1 -1
- package/dist-embed/chunks/{mr-IN-CRQNXWMA-BKWa9nzZ.js → mr-IN-CRQNXWMA-tndp8BXC.js} +1 -1
- package/dist-embed/chunks/{my-MM-5M5IBNSE-bwAzOWhV.js → my-MM-5M5IBNSE-sispDhO4.js} +1 -1
- package/dist-embed/chunks/{nb-NO-T6EIAALU-CkpsChor.js → nb-NO-T6EIAALU-CjBurOuS.js} +1 -1
- package/dist-embed/chunks/{nl-NL-IS3SIHDZ-COVk4rlb.js → nl-NL-IS3SIHDZ-B58EVmr3.js} +1 -1
- package/dist-embed/chunks/{nn-NO-6E72VCQL-DlyFF-C1.js → nn-NO-6E72VCQL-D1p7LZSq.js} +1 -1
- package/dist-embed/chunks/{oc-FR-POXYY2M6-CFDosV2C.js → oc-FR-POXYY2M6-C1M8sddP.js} +1 -1
- package/dist-embed/chunks/{pa-IN-N4M65BXN-BKRB3O0U.js → pa-IN-N4M65BXN-D8ESzASl.js} +1 -1
- package/dist-embed/chunks/{percentages-BXMCSKIN-D7pnqUsZ.js → percentages-BXMCSKIN-BihMTsFR.js} +3 -3
- package/dist-embed/chunks/{pl-PL-T2D74RX3-C5r2Tds7.js → pl-PL-T2D74RX3-DQGe6LHC.js} +1 -1
- package/dist-embed/chunks/{pt-BR-5N22H2LF-CDnT98bW.js → pt-BR-5N22H2LF-CvXBgZQs.js} +1 -1
- package/dist-embed/chunks/{pt-PT-UZXXM6DQ-C6c4SPk1.js → pt-PT-UZXXM6DQ-BMnnlIUJ.js} +1 -1
- package/dist-embed/chunks/{ro-RO-JPDTUUEW-CW6pbzEZ.js → ro-RO-JPDTUUEW-DZ-9UUH7.js} +1 -1
- package/dist-embed/chunks/{ru-RU-B4JR7IUQ-CbaAJG0w.js → ru-RU-B4JR7IUQ-JlRBwjob.js} +1 -1
- package/dist-embed/chunks/{si-LK-N5RQ5JYF-BZkUjpXF.js → si-LK-N5RQ5JYF-Bx8kHnkd.js} +1 -1
- package/dist-embed/chunks/{sk-SK-C5VTKIMK-DsfWgpPU.js → sk-SK-C5VTKIMK-xsfEfbZb.js} +1 -1
- package/dist-embed/chunks/{sl-SI-NN7IZMDC-Kx0FqxKU.js → sl-SI-NN7IZMDC-SQYUECnr.js} +1 -1
- package/dist-embed/chunks/{subset-shared.chunk-DM3l9OuH.js → subset-shared.chunk-Ch6j8nKC.js} +1 -1
- package/dist-embed/chunks/{subset-worker.chunk-4w4yibm3.js → subset-worker.chunk-pJjck5pR.js} +1 -1
- package/dist-embed/chunks/{sv-SE-XGPEYMSR-DY0ryO-L.js → sv-SE-XGPEYMSR-BxcjSHE2.js} +1 -1
- package/dist-embed/chunks/{ta-IN-2NMHFXQM-CWpIs0i0.js → ta-IN-2NMHFXQM-rItSkDUS.js} +1 -1
- package/dist-embed/chunks/{th-TH-HPSO5L25-Cwkcfb3u.js → th-TH-HPSO5L25-B7UzESFj.js} +1 -1
- package/dist-embed/chunks/{tr-TR-DEFEU3FU-BqJZZl-Y.js → tr-TR-DEFEU3FU-Ci6oNfEX.js} +1 -1
- package/dist-embed/chunks/{uk-UA-QMV73CPH-Dg213mG4.js → uk-UA-QMV73CPH-C9csqvO6.js} +1 -1
- package/dist-embed/chunks/{vi-VN-M7AON7JQ-DY94NKxF.js → vi-VN-M7AON7JQ-DRAj2kZj.js} +1 -1
- package/dist-embed/chunks/{zh-CN-LNUGB5OW-B6I9A7sA.js → zh-CN-LNUGB5OW-DLOyW8hH.js} +1 -1
- package/dist-embed/chunks/{zh-HK-E62DVLB3-D3oqgDOt.js → zh-HK-E62DVLB3-DwC6_2Ws.js} +1 -1
- package/dist-embed/chunks/{zh-TW-RAJ6MFWO-CYwgY0Jo.js → zh-TW-RAJ6MFWO-DEMugIwa.js} +1 -1
- package/dist-embed/datalayer-app-main.js +156 -156
- package/lib/apps/apps/appspec.d.ts +13 -0
- package/lib/apps/apps/appspec.js +93 -0
- package/lib/apps/apps/checks.d.ts +1 -1
- package/lib/apps/apps/checks.js +2 -2
- package/lib/apps/apps/customComponents.js +1 -1
- package/lib/apps/core/canvasBlocks.d.ts +3 -3
- package/lib/apps/core/canvasParts.d.ts +6 -4
- package/lib/apps/core/index.d.ts +8 -0
- package/lib/apps/embed/AppEmbed.js +13 -20
- package/lib/apps/embed/embedSession.d.ts +8 -4
- package/lib/apps/embed/embedSession.js +16 -9
- package/lib/apps/embed/hostBridge.d.ts +47 -13
- package/lib/apps/embed/hostBridge.js +135 -18
- package/lib/apps/index.d.ts +1 -1
- package/lib/apps/index.js +1 -1
- package/lib/apps/plugins/a2ui-surface/AnswerShows.d.ts +21 -0
- package/lib/apps/plugins/a2ui-surface/AnswerShows.js +29 -0
- package/lib/apps/plugins/a2ui-surface/SurfaceToolResult.js +4 -7
- package/lib/apps/plugins/a2ui-surface/index.d.ts +2 -1
- package/lib/apps/plugins/a2ui-surface/index.js +10 -1
- package/lib/apps/plugins/a2ui-surface/toolResult.d.ts +20 -11
- package/lib/apps/plugins/a2ui-surface/toolResult.js +27 -11
- package/lib/apps/plugins/app-components/index.d.ts +1 -1
- package/lib/apps/plugins/app-components/index.js +2 -2
- package/lib/apps/plugins/app-elements/AppElements.d.ts +3 -3
- package/lib/apps/plugins/app-elements/AppElements.js +5 -20
- package/lib/apps/plugins/app-elements/QuestionView.d.ts +30 -0
- package/lib/apps/plugins/app-elements/QuestionView.js +70 -0
- package/lib/apps/plugins/app-elements/index.d.ts +7 -4
- package/lib/apps/plugins/app-elements/index.js +14 -5
- package/lib/apps/plugins/app-page/appPageModel.d.ts +6 -8
- package/lib/apps/plugins/app-page/appPageModel.js +0 -2
- package/lib/apps/plugins/app-page/pageRun.d.ts +2 -2
- package/lib/apps/plugins/app-page/pageRun.js +3 -3
- package/lib/apps/plugins/canvas-blocks/index.d.ts +13 -13
- package/lib/apps/plugins/canvas-blocks/index.js +24 -24
- package/lib/apps/plugins/canvas-parts/index.d.ts +7 -1
- package/lib/apps/plugins/canvas-parts/index.js +36 -0
- package/lib/apps/plugins/chat/ChatView.js +29 -3
- package/lib/chat/base/ChatBase.js +264 -43
- package/lib/chat/base/loopElement.d.ts +16 -17
- package/lib/chat/base/loopElement.js +11 -27
- package/lib/chat/base/loopStep.d.ts +15 -15
- package/lib/chat/base/loopStep.js +10 -13
- package/lib/chat/base/loopWindow.d.ts +15 -20
- package/lib/chat/base/loopWindow.js +26 -27
- package/lib/chat/base/uiFiles.d.ts +29 -0
- package/lib/chat/base/uiFiles.js +69 -0
- package/lib/chat/base/uiOps.d.ts +96 -0
- package/lib/chat/base/uiOps.js +251 -0
- package/lib/chat/messages/ChatMessageList.d.ts +13 -1
- package/lib/chat/messages/ChatMessageList.js +4 -4
- package/lib/components/a2ui/datalayer/Chart.d.ts +1 -1
- package/lib/components/a2ui/datalayer/schema.js +1 -1
- package/lib/components/teams/AnswerSurfaces.d.ts +3 -2
- package/lib/components/teams/AnswerSurfaces.js +7 -7
- package/lib/components/teams/useA2ATeam.d.ts +1 -1
- package/lib/config/AgentConfiguration.d.ts +1 -1
- package/lib/examples/ProtocolSignedRequestsExample.d.ts +10 -0
- package/lib/examples/ProtocolSignedRequestsExample.js +41 -0
- package/lib/examples/ProtocolUcpCheckoutExample.d.ts +10 -0
- package/lib/examples/ProtocolUcpCheckoutExample.js +59 -0
- package/lib/examples/example-selector.js +2 -0
- package/lib/examples/exampleGroups.d.ts +1 -1
- package/lib/examples/exampleGroups.js +4 -0
- package/lib/examples/index.d.ts +2 -0
- package/lib/examples/index.js +2 -0
- package/lib/examples/protocols/signedRequestsDemo.d.ts +27 -0
- package/lib/examples/protocols/signedRequestsDemo.js +59 -0
- package/lib/examples/protocols/ucpCheckoutDemo.d.ts +36 -0
- package/lib/examples/protocols/ucpCheckoutDemo.js +68 -0
- package/lib/protocols/AGUIAdapter.d.ts +7 -0
- package/lib/protocols/AGUIAdapter.js +43 -3
- package/lib/protocols/a2a/index.js +1 -0
- package/lib/protocols/agentic-commerce/index.js +1 -0
- package/lib/protocols/core.d.ts +6 -0
- package/lib/protocols/mcp/index.js +1 -0
- package/lib/protocols/pact/index.js +1 -0
- package/lib/protocols/ucp/index.js +1 -0
- package/lib/protocols/ucp/merchant.d.ts +15 -0
- package/lib/protocols/ucp/merchant.js +257 -0
- package/lib/protocols/web-bot-auth/index.js +1 -0
- package/lib/specs/agents/agents.js +338 -169
- package/lib/specs/apps.js +134 -6
- package/lib/specs/appspecSchema.js +59 -0
- package/lib/specs/cogs.js +6 -3
- package/lib/specs/index.d.ts +2 -1
- package/lib/specs/index.js +2 -1
- package/lib/specs/protocols.d.ts +24 -0
- package/lib/specs/protocols.js +197 -0
- package/lib/specs/ui.d.ts +21 -0
- package/lib/specs/{uiPlugins.js → ui.js} +14 -14
- package/lib/types/agentspecs.d.ts +69 -5
- package/lib/types/messages.d.ts +7 -0
- package/lib/types/protocol.d.ts +15 -0
- package/package.json +2 -2
- package/scripts/codegen/generate_agents.py +43 -8
- package/scripts/codegen/generate_protocols.py +207 -0
- package/scripts/codegen/generate_sandbox_agents.py +1 -1
- package/scripts/codegen/{generate_ui_plugins.py → generate_ui.py} +40 -40
- package/scripts/gen_workers.py +2 -2
- package/dist-embed/chunks/ChatView-D41Nx5rC.js +0 -32
- package/lib/chat/base/loopMessage.d.ts +0 -37
- package/lib/chat/base/loopMessage.js +0 -37
- package/lib/specs/uiPlugins.d.ts +0 -21
|
@@ -6,21 +6,25 @@
|
|
|
6
6
|
* Window messages (LOOP P-25): what an application's code tells the page it
|
|
7
7
|
* sits in, outside the conversation — ``session.send_window_message(data)``.
|
|
8
8
|
*
|
|
9
|
-
* The session
|
|
10
|
-
* `{data}`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The session says it with a `reactor.ui` `window` op named `message`, its
|
|
10
|
+
* `args` `{data}` (REACTOR_UI RU-3), on the conversation's ui socket (RU-W). The chat does not draw it: it hands
|
|
11
|
+
* it to whoever listens here — the embed's host bridge, which raises it on
|
|
12
|
+
* the element as its `window-message` event. A window op of another name is
|
|
13
|
+
* not one this page acts on: refused, it throws.
|
|
13
14
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
import { AGUI_NAME } from '@datalayer/reactor/ui';
|
|
16
|
+
/** The name of the window op that hands the page a message. */
|
|
17
|
+
export const WINDOW_MESSAGE = 'message';
|
|
18
|
+
/** What a window op hands the page. Throws for a window op of another name. */
|
|
19
|
+
export function windowMessageOf(op) {
|
|
20
|
+
if (op.body.name !== WINDOW_MESSAGE) {
|
|
21
|
+
throw new Error(`A window op named ${op.body.name} is not one this page acts on`);
|
|
22
|
+
}
|
|
23
|
+
const args = op.body.args;
|
|
24
|
+
if (!args || !('data' in args)) {
|
|
25
|
+
throw new Error('A window message says no data');
|
|
26
|
+
}
|
|
27
|
+
return args.data;
|
|
24
28
|
}
|
|
25
29
|
const listeners = new Set();
|
|
26
30
|
/** Hand a window message to whoever listens. */
|
|
@@ -36,12 +40,12 @@ export function onLoopWindowMessage(listener) {
|
|
|
36
40
|
};
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
43
|
+
* Why the turn a window message posted by the page ran failed — its
|
|
44
|
+
* `RUN_ERROR`'s sentence — or `null`. What its code tells the page back comes
|
|
45
|
+
* on the conversation's ui socket (REACTOR_UI RU-W), never on this stream: a
|
|
46
|
+
* `reactor.ui` event here is refused.
|
|
42
47
|
*/
|
|
43
|
-
export function
|
|
44
|
-
const said = [];
|
|
48
|
+
export function windowTurnErrorOf(stream) {
|
|
45
49
|
let error = null;
|
|
46
50
|
for (const block of stream.split('\n\n')) {
|
|
47
51
|
const data = block
|
|
@@ -61,14 +65,9 @@ export function windowTurnOf(stream) {
|
|
|
61
65
|
if (event.type === 'RUN_ERROR') {
|
|
62
66
|
error = String(event.message ?? 'The turn failed.');
|
|
63
67
|
}
|
|
64
|
-
else if (event.type === 'CUSTOM') {
|
|
65
|
-
|
|
66
|
-
type: String(event.name),
|
|
67
|
-
data: event.value,
|
|
68
|
-
});
|
|
69
|
-
if (message)
|
|
70
|
-
said.push(message.data);
|
|
68
|
+
else if (event.type === 'CUSTOM' && event.name === AGUI_NAME) {
|
|
69
|
+
throw new Error('A reactor.ui op came on the run stream: it comes over the ui socket.');
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
|
-
return
|
|
72
|
+
return error;
|
|
74
73
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A conversation's files as references (REACTOR_UI RU-6).
|
|
3
|
+
*
|
|
4
|
+
* What the page holds of a file it was given — File upload and the composer
|
|
5
|
+
* read it as `{name, type, data_url}` — is uploaded to the application's
|
|
6
|
+
* runtime before the run that names it (Reactor's `uploadFile`), and the run
|
|
7
|
+
* carries the ref it answers (`forwardedProps.loop.files`): a file's bytes
|
|
8
|
+
* never ride in the run.
|
|
9
|
+
*
|
|
10
|
+
* @module chat/base/uiFiles
|
|
11
|
+
*/
|
|
12
|
+
import { type FileRef } from '@datalayer/reactor/ui';
|
|
13
|
+
/** How a file is uploaded to the conversation: its ref. */
|
|
14
|
+
export type UploadToConversation = (file: Blob & {
|
|
15
|
+
name: string;
|
|
16
|
+
}) => Promise<FileRef>;
|
|
17
|
+
/** A file's bytes from the data URL the page read it as. */
|
|
18
|
+
export declare function blobOfDataUrl(dataUrl: string, name: string, type?: string): Blob & {
|
|
19
|
+
name: string;
|
|
20
|
+
};
|
|
21
|
+
/** Whether a run's `forwardedProps` names files. */
|
|
22
|
+
export declare function namesFiles(forwardedProps: Record<string, unknown> | undefined): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The run's `forwardedProps` with its files uploaded: each file the page
|
|
25
|
+
* holds replaced by the ref its upload answered, a ref kept as it is.
|
|
26
|
+
*
|
|
27
|
+
* @throws for a file that is neither, or an upload refused (its sentence).
|
|
28
|
+
*/
|
|
29
|
+
export declare function withUploadedFiles(forwardedProps: Record<string, unknown>, upload: UploadToConversation): Promise<Record<string, unknown>>;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* A conversation's files as references (REACTOR_UI RU-6).
|
|
7
|
+
*
|
|
8
|
+
* What the page holds of a file it was given — File upload and the composer
|
|
9
|
+
* read it as `{name, type, data_url}` — is uploaded to the application's
|
|
10
|
+
* runtime before the run that names it (Reactor's `uploadFile`), and the run
|
|
11
|
+
* carries the ref it answers (`forwardedProps.loop.files`): a file's bytes
|
|
12
|
+
* never ride in the run.
|
|
13
|
+
*
|
|
14
|
+
* @module chat/base/uiFiles
|
|
15
|
+
*/
|
|
16
|
+
import { isFileRef } from '@datalayer/reactor/ui';
|
|
17
|
+
/** A file's bytes from the data URL the page read it as. */
|
|
18
|
+
export function blobOfDataUrl(dataUrl, name, type) {
|
|
19
|
+
const [head, body] = [
|
|
20
|
+
dataUrl.slice(0, dataUrl.indexOf(',')),
|
|
21
|
+
dataUrl.slice(dataUrl.indexOf(',') + 1),
|
|
22
|
+
];
|
|
23
|
+
if (!head.startsWith('data:') || dataUrl.indexOf(',') < 0) {
|
|
24
|
+
throw new Error(`${name} could not be read: it is not a data URL.`);
|
|
25
|
+
}
|
|
26
|
+
const mediaType = type || head.slice('data:'.length).split(';')[0];
|
|
27
|
+
// Backed by an ArrayBuffer, as Blob takes it (a SharedArrayBuffer-backed view it refuses).
|
|
28
|
+
let bytes;
|
|
29
|
+
if (head.endsWith(';base64')) {
|
|
30
|
+
const binary = atob(body);
|
|
31
|
+
bytes = new Uint8Array(binary.length);
|
|
32
|
+
for (let index = 0; index < binary.length; index += 1) {
|
|
33
|
+
bytes[index] = binary.charCodeAt(index);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
bytes = new TextEncoder().encode(decodeURIComponent(body));
|
|
38
|
+
}
|
|
39
|
+
return Object.assign(new Blob([bytes], { type: mediaType }), { name });
|
|
40
|
+
}
|
|
41
|
+
/** Whether a run's `forwardedProps` names files. */
|
|
42
|
+
export function namesFiles(forwardedProps) {
|
|
43
|
+
const files = forwardedProps?.loop
|
|
44
|
+
?.files;
|
|
45
|
+
return Array.isArray(files) && files.length > 0;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* The run's `forwardedProps` with its files uploaded: each file the page
|
|
49
|
+
* holds replaced by the ref its upload answered, a ref kept as it is.
|
|
50
|
+
*
|
|
51
|
+
* @throws for a file that is neither, or an upload refused (its sentence).
|
|
52
|
+
*/
|
|
53
|
+
export async function withUploadedFiles(forwardedProps, upload) {
|
|
54
|
+
const loop = forwardedProps.loop;
|
|
55
|
+
const files = loop.files;
|
|
56
|
+
const refs = [];
|
|
57
|
+
for (const given of files) {
|
|
58
|
+
if (isFileRef(given)) {
|
|
59
|
+
refs.push(given);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
const held = given;
|
|
63
|
+
if (typeof held.name !== 'string' || typeof held.data_url !== 'string') {
|
|
64
|
+
throw new Error('A file given is neither the page’s nor a ref.');
|
|
65
|
+
}
|
|
66
|
+
refs.push(await upload(blobOfDataUrl(held.data_url, held.name, typeof held.type === 'string' ? held.type : undefined)));
|
|
67
|
+
}
|
|
68
|
+
return { ...forwardedProps, loop: { ...loop, files: refs } };
|
|
69
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a server says to a conversation's page, as `reactor.ui` ops
|
|
3
|
+
* (REACTOR_UI RU-2, RU-3), and what the page presses back (RU-W).
|
|
4
|
+
*
|
|
5
|
+
* The ops arrive on the conversation's ui socket (Reactor's `UiSocket`,
|
|
6
|
+
* one per session, beside the run stream that carries the conversation's
|
|
7
|
+
* text); `routeUiOp` says where each goes:
|
|
8
|
+
*
|
|
9
|
+
* - a `window` op to the page the conversation sits in (`loopWindow`);
|
|
10
|
+
* - a step's display to its row (`loopStep`), patched as it streams;
|
|
11
|
+
* - a display that says it shows in a panel or on a page to the elements
|
|
12
|
+
* open (`loopElement`), a `remove` of its id closing it;
|
|
13
|
+
* - everything else to the messages (`withUiOp`): a display under a message
|
|
14
|
+
* (`data.message_id`) kept on it (`ChatMessage.displays`) and drawn by the
|
|
15
|
+
* renderer a plugin contributed for its type; a `meta` op's `author` the
|
|
16
|
+
* message's voice; a `remove` of a message's id the message gone.
|
|
17
|
+
*
|
|
18
|
+
* An op about a message that has not arrived on the run stream yet — the
|
|
19
|
+
* two are separate connections — waits until it has (`placeable`). A press
|
|
20
|
+
* of what a display offered goes up the socket (`displayPressOf`), and the
|
|
21
|
+
* run that answers it names it only (`withPress`).
|
|
22
|
+
*
|
|
23
|
+
* Pure: the chat applies it to what it draws, and to its store. Nothing it
|
|
24
|
+
* was not told to expect is guessed at: an op it cannot place throws.
|
|
25
|
+
*
|
|
26
|
+
* @module chat/base/uiOps
|
|
27
|
+
*/
|
|
28
|
+
import { type Display, type UiAction, type UiModel, type UiOp } from '@datalayer/reactor/ui';
|
|
29
|
+
import type { MessageSpeaker } from '../../types/messages';
|
|
30
|
+
import { type LoopElementChange } from './loopElement';
|
|
31
|
+
import { type LoopStep } from './loopStep';
|
|
32
|
+
/** The message a display is under: its `data.message_id`. */
|
|
33
|
+
export declare function messageOfDisplay(display: Display): string;
|
|
34
|
+
/** Who a message is by, as the chat draws a named voice. */
|
|
35
|
+
export declare function speakerOf(author: string): MessageSpeaker;
|
|
36
|
+
type WithDisplays = {
|
|
37
|
+
id?: string;
|
|
38
|
+
displays?: Display[];
|
|
39
|
+
speaker?: MessageSpeaker;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* The messages with an op applied: a display shown (or asked) kept on its
|
|
43
|
+
* message, replacing one of its id; patched, appended to or removed where it
|
|
44
|
+
* is. A `remove` of a message's own id takes the message away, with what it
|
|
45
|
+
* showed; a `meta` op about a message gives it its `author`. `state` ops are
|
|
46
|
+
* not about the messages: they come back as they were.
|
|
47
|
+
*
|
|
48
|
+
* Throws for a display under no message here, for a change to a display no
|
|
49
|
+
* message shows, for a removal of nothing here, and for a `meta` op that
|
|
50
|
+
* says anything but a message's author: the server said something the page
|
|
51
|
+
* never saw, or does not read.
|
|
52
|
+
*/
|
|
53
|
+
export declare function withUiOp<T extends WithDisplays>(items: T[], op: UiOp): T[];
|
|
54
|
+
/** Where an op goes (`routeUiOp`). */
|
|
55
|
+
export type RoutedUiOp = {
|
|
56
|
+
to: 'window';
|
|
57
|
+
data: unknown;
|
|
58
|
+
} | {
|
|
59
|
+
to: 'step';
|
|
60
|
+
step: LoopStep;
|
|
61
|
+
} | {
|
|
62
|
+
to: 'element';
|
|
63
|
+
change: LoopElementChange;
|
|
64
|
+
} | {
|
|
65
|
+
to: 'messages';
|
|
66
|
+
op: UiOp;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Where an op goes, and the steps kept after it: a step's display is kept
|
|
70
|
+
* (in a Reactor `UiModel`) so a patch of its output applies to it.
|
|
71
|
+
*
|
|
72
|
+
* `elements` is the ids of the elements shown in this conversation: a
|
|
73
|
+
* `remove` of one closes it (a no-op when the person closed it already).
|
|
74
|
+
*/
|
|
75
|
+
export declare function routeUiOp(op: UiOp, steps: UiModel, elements: ReadonlySet<string>): {
|
|
76
|
+
routed: RoutedUiOp;
|
|
77
|
+
steps: UiModel;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Whether the messages hold what an op is about: the message a display is
|
|
81
|
+
* under, the message a `meta` op names, the display a change is to, what a
|
|
82
|
+
* `remove` takes away. An op about what has not arrived yet waits.
|
|
83
|
+
*/
|
|
84
|
+
export declare function placeable<T extends WithDisplays>(items: readonly T[], op: UiOp): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* The press a run's `forwardedProps` carries for a display's button — what
|
|
87
|
+
* `AnswerShowsView` hands the chat — or `null` for a run that presses
|
|
88
|
+
* nothing of a display's. It goes up the ui socket, never in the run.
|
|
89
|
+
*/
|
|
90
|
+
export declare function displayPressOf(forwardedProps: Record<string, unknown> | undefined): UiAction | null;
|
|
91
|
+
/**
|
|
92
|
+
* The run's `forwardedProps` once its press was checked on the socket: the
|
|
93
|
+
* press named by the id the runtime answered, the action itself gone.
|
|
94
|
+
*/
|
|
95
|
+
export declare function withPress(forwardedProps: Record<string, unknown>, answered: unknown): Record<string, unknown>;
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* What a server says to a conversation's page, as `reactor.ui` ops
|
|
7
|
+
* (REACTOR_UI RU-2, RU-3), and what the page presses back (RU-W).
|
|
8
|
+
*
|
|
9
|
+
* The ops arrive on the conversation's ui socket (Reactor's `UiSocket`,
|
|
10
|
+
* one per session, beside the run stream that carries the conversation's
|
|
11
|
+
* text); `routeUiOp` says where each goes:
|
|
12
|
+
*
|
|
13
|
+
* - a `window` op to the page the conversation sits in (`loopWindow`);
|
|
14
|
+
* - a step's display to its row (`loopStep`), patched as it streams;
|
|
15
|
+
* - a display that says it shows in a panel or on a page to the elements
|
|
16
|
+
* open (`loopElement`), a `remove` of its id closing it;
|
|
17
|
+
* - everything else to the messages (`withUiOp`): a display under a message
|
|
18
|
+
* (`data.message_id`) kept on it (`ChatMessage.displays`) and drawn by the
|
|
19
|
+
* renderer a plugin contributed for its type; a `meta` op's `author` the
|
|
20
|
+
* message's voice; a `remove` of a message's id the message gone.
|
|
21
|
+
*
|
|
22
|
+
* An op about a message that has not arrived on the run stream yet — the
|
|
23
|
+
* two are separate connections — waits until it has (`placeable`). A press
|
|
24
|
+
* of what a display offered goes up the socket (`displayPressOf`), and the
|
|
25
|
+
* run that answers it names it only (`withPress`).
|
|
26
|
+
*
|
|
27
|
+
* Pure: the chat applies it to what it draws, and to its store. Nothing it
|
|
28
|
+
* was not told to expect is guessed at: an op it cannot place throws.
|
|
29
|
+
*
|
|
30
|
+
* @module chat/base/uiOps
|
|
31
|
+
*/
|
|
32
|
+
import { applyPatch, applyUiOp, } from '@datalayer/reactor/ui';
|
|
33
|
+
import { elementOfDisplay } from './loopElement';
|
|
34
|
+
import { isStepDisplay, stepOfDisplay } from './loopStep';
|
|
35
|
+
import { windowMessageOf } from './loopWindow';
|
|
36
|
+
/** The message a display is under: its `data.message_id`. */
|
|
37
|
+
export function messageOfDisplay(display) {
|
|
38
|
+
const id = display.data?.message_id;
|
|
39
|
+
if (typeof id !== 'string' || !id) {
|
|
40
|
+
throw new Error(`Display ${display.id} says no message it is under`);
|
|
41
|
+
}
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
44
|
+
/** Who a message is by, as the chat draws a named voice. */
|
|
45
|
+
export function speakerOf(author) {
|
|
46
|
+
return { id: `author:${author}`, name: author };
|
|
47
|
+
}
|
|
48
|
+
/** The messages with one display changed by `change`, or taken away when it answers nothing. */
|
|
49
|
+
function changed(items, id, change) {
|
|
50
|
+
const at = items.findIndex(item => (item.displays ?? []).some(display => display.id === id));
|
|
51
|
+
if (at < 0) {
|
|
52
|
+
throw new Error(`Display ${id} is shown under no message here`);
|
|
53
|
+
}
|
|
54
|
+
return items.map((item, index) => {
|
|
55
|
+
if (index !== at)
|
|
56
|
+
return item;
|
|
57
|
+
const displays = [];
|
|
58
|
+
for (const display of item.displays ?? []) {
|
|
59
|
+
if (display.id !== id) {
|
|
60
|
+
displays.push(display);
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const next = change(display);
|
|
64
|
+
if (next)
|
|
65
|
+
displays.push({ ...next, id });
|
|
66
|
+
}
|
|
67
|
+
return { ...item, displays };
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
function replaced(displays, display) {
|
|
71
|
+
const at = displays.findIndex(one => one.id === display.id);
|
|
72
|
+
return at < 0
|
|
73
|
+
? [...displays, display]
|
|
74
|
+
: displays.map((one, index) => (index === at ? display : one));
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The messages with an op applied: a display shown (or asked) kept on its
|
|
78
|
+
* message, replacing one of its id; patched, appended to or removed where it
|
|
79
|
+
* is. A `remove` of a message's own id takes the message away, with what it
|
|
80
|
+
* showed; a `meta` op about a message gives it its `author`. `state` ops are
|
|
81
|
+
* not about the messages: they come back as they were.
|
|
82
|
+
*
|
|
83
|
+
* Throws for a display under no message here, for a change to a display no
|
|
84
|
+
* message shows, for a removal of nothing here, and for a `meta` op that
|
|
85
|
+
* says anything but a message's author: the server said something the page
|
|
86
|
+
* never saw, or does not read.
|
|
87
|
+
*/
|
|
88
|
+
export function withUiOp(items, op) {
|
|
89
|
+
switch (op.op) {
|
|
90
|
+
case 'show':
|
|
91
|
+
case 'ask': {
|
|
92
|
+
const shown = op.body;
|
|
93
|
+
const messageId = messageOfDisplay(shown);
|
|
94
|
+
if (!items.some(item => item.id === messageId)) {
|
|
95
|
+
throw new Error(`Display ${shown.id} is under message ${messageId}, which is not here`);
|
|
96
|
+
}
|
|
97
|
+
return items.map(item => item.id === messageId
|
|
98
|
+
? { ...item, displays: replaced(item.displays ?? [], shown) }
|
|
99
|
+
: item);
|
|
100
|
+
}
|
|
101
|
+
case 'remove': {
|
|
102
|
+
const showsIt = items.some(item => (item.displays ?? []).some(display => display.id === op.id));
|
|
103
|
+
if (showsIt)
|
|
104
|
+
return changed(items, op.id, () => undefined);
|
|
105
|
+
if (!items.some(item => item.id === op.id)) {
|
|
106
|
+
throw new Error(`Nothing here is ${op.id}: it cannot be removed`);
|
|
107
|
+
}
|
|
108
|
+
return items.filter(item => item.id !== op.id);
|
|
109
|
+
}
|
|
110
|
+
case 'patch': {
|
|
111
|
+
const operations = op.body;
|
|
112
|
+
return changed(items, op.id, display => applyPatch(display, operations));
|
|
113
|
+
}
|
|
114
|
+
case 'append': {
|
|
115
|
+
const { path, value } = op.body;
|
|
116
|
+
return changed(items, op.id, display => applyPatch(display, [{ op: 'add', path: `${path}/-`, value }]));
|
|
117
|
+
}
|
|
118
|
+
case 'meta': {
|
|
119
|
+
const author = op.body.author;
|
|
120
|
+
const keys = Object.keys(op.body);
|
|
121
|
+
if (!op.id ||
|
|
122
|
+
keys.length !== 1 ||
|
|
123
|
+
typeof author !== 'string' ||
|
|
124
|
+
!author.trim()) {
|
|
125
|
+
throw new Error(`A meta op says ${JSON.stringify(op.body)}: only a message's author is read`);
|
|
126
|
+
}
|
|
127
|
+
if (!items.some(item => item.id === op.id)) {
|
|
128
|
+
throw new Error(`Message ${op.id} is not here: it has no author to say`);
|
|
129
|
+
}
|
|
130
|
+
return items.map(item => item.id === op.id
|
|
131
|
+
? { ...item, speaker: speakerOf(author.trim()) }
|
|
132
|
+
: item);
|
|
133
|
+
}
|
|
134
|
+
case 'window':
|
|
135
|
+
throw new Error('A window op is for the page, not the messages');
|
|
136
|
+
default:
|
|
137
|
+
return items;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Where an op goes, and the steps kept after it: a step's display is kept
|
|
142
|
+
* (in a Reactor `UiModel`) so a patch of its output applies to it.
|
|
143
|
+
*
|
|
144
|
+
* `elements` is the ids of the elements shown in this conversation: a
|
|
145
|
+
* `remove` of one closes it (a no-op when the person closed it already).
|
|
146
|
+
*/
|
|
147
|
+
export function routeUiOp(op, steps, elements) {
|
|
148
|
+
switch (op.op) {
|
|
149
|
+
case 'window':
|
|
150
|
+
return { routed: { to: 'window', data: windowMessageOf(op) }, steps };
|
|
151
|
+
case 'show':
|
|
152
|
+
case 'ask': {
|
|
153
|
+
if (isStepDisplay(op.body)) {
|
|
154
|
+
const kept = applyUiOp(steps, op);
|
|
155
|
+
return {
|
|
156
|
+
routed: { to: 'step', step: stepOfDisplay(op.body) },
|
|
157
|
+
steps: kept,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const element = elementOfDisplay(op.body);
|
|
161
|
+
if (element) {
|
|
162
|
+
return { routed: { to: 'element', change: element }, steps };
|
|
163
|
+
}
|
|
164
|
+
return { routed: { to: 'messages', op }, steps };
|
|
165
|
+
}
|
|
166
|
+
case 'patch':
|
|
167
|
+
case 'append': {
|
|
168
|
+
if (op.id in steps.displays) {
|
|
169
|
+
const kept = applyUiOp(steps, op);
|
|
170
|
+
return {
|
|
171
|
+
routed: { to: 'step', step: stepOfDisplay(kept.displays[op.id]) },
|
|
172
|
+
steps: kept,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return { routed: { to: 'messages', op }, steps };
|
|
176
|
+
}
|
|
177
|
+
case 'remove': {
|
|
178
|
+
if (op.id in steps.displays) {
|
|
179
|
+
throw new Error(`Step ${op.id} is never removed: it ends`);
|
|
180
|
+
}
|
|
181
|
+
if (elements.has(op.id)) {
|
|
182
|
+
return {
|
|
183
|
+
routed: { to: 'element', change: { id: op.id, closed: true } },
|
|
184
|
+
steps,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
return { routed: { to: 'messages', op }, steps };
|
|
188
|
+
}
|
|
189
|
+
default:
|
|
190
|
+
return { routed: { to: 'messages', op }, steps };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Whether the messages hold what an op is about: the message a display is
|
|
195
|
+
* under, the message a `meta` op names, the display a change is to, what a
|
|
196
|
+
* `remove` takes away. An op about what has not arrived yet waits.
|
|
197
|
+
*/
|
|
198
|
+
export function placeable(items, op) {
|
|
199
|
+
const hasItem = (id) => !!id && items.some(item => item.id === id);
|
|
200
|
+
const hasDisplay = (id) => !!id &&
|
|
201
|
+
items.some(item => (item.displays ?? []).some(display => display.id === id));
|
|
202
|
+
switch (op.op) {
|
|
203
|
+
case 'show':
|
|
204
|
+
case 'ask': {
|
|
205
|
+
const under = op.body.data?.message_id;
|
|
206
|
+
return typeof under === 'string' && hasItem(under);
|
|
207
|
+
}
|
|
208
|
+
case 'meta':
|
|
209
|
+
return hasItem(op.id);
|
|
210
|
+
case 'patch':
|
|
211
|
+
case 'append':
|
|
212
|
+
return hasDisplay(op.id);
|
|
213
|
+
case 'remove':
|
|
214
|
+
return hasDisplay(op.id) || hasItem(op.id);
|
|
215
|
+
default:
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The press a run's `forwardedProps` carries for a display's button — what
|
|
221
|
+
* `AnswerShowsView` hands the chat — or `null` for a run that presses
|
|
222
|
+
* nothing of a display's. It goes up the ui socket, never in the run.
|
|
223
|
+
*/
|
|
224
|
+
export function displayPressOf(forwardedProps) {
|
|
225
|
+
const loop = forwardedProps?.loop;
|
|
226
|
+
const action = loop?.action;
|
|
227
|
+
if (!action || typeof action.display_id !== 'string')
|
|
228
|
+
return null;
|
|
229
|
+
if (typeof action.name !== 'string' || typeof action.token !== 'string') {
|
|
230
|
+
throw new Error('A press of a display says its action and its token');
|
|
231
|
+
}
|
|
232
|
+
return {
|
|
233
|
+
action: action.name,
|
|
234
|
+
display_id: action.display_id,
|
|
235
|
+
token: action.token,
|
|
236
|
+
payload: action.payload ?? {},
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The run's `forwardedProps` once its press was checked on the socket: the
|
|
241
|
+
* press named by the id the runtime answered, the action itself gone.
|
|
242
|
+
*/
|
|
243
|
+
export function withPress(forwardedProps, answered) {
|
|
244
|
+
const press = answered?.press;
|
|
245
|
+
if (typeof press !== 'string' || !press) {
|
|
246
|
+
throw new Error('The runtime checked the press but named none');
|
|
247
|
+
}
|
|
248
|
+
const loop = { ...forwardedProps.loop };
|
|
249
|
+
delete loop.action;
|
|
250
|
+
return { ...forwardedProps, loop: { ...loop, press } };
|
|
251
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode, type RefObject } from 'react';
|
|
2
|
+
import { type Display, type UiAct, type UiUpload } from '@datalayer/reactor/ui';
|
|
2
3
|
import { type ChatDensity } from './ChatMarkdown';
|
|
3
4
|
import { type MarkedMcpServer } from '../marks';
|
|
4
5
|
import type { DisplayItem, AvatarConfig } from '../../types/chat';
|
|
@@ -13,6 +14,17 @@ export interface ToolApprovalConfig {
|
|
|
13
14
|
export interface ChatMessageListProps {
|
|
14
15
|
/** All display items (messages + tool calls) */
|
|
15
16
|
displayItems: DisplayItem[];
|
|
17
|
+
/**
|
|
18
|
+
* How a message's display presses what it offered (REACTOR_UI RU-4, RU-W):
|
|
19
|
+
* the chat sends it up the conversation's ui socket. Absent, its
|
|
20
|
+
* displays offer nothing to press.
|
|
21
|
+
*/
|
|
22
|
+
actOn?: (display: Display) => UiAct;
|
|
23
|
+
/**
|
|
24
|
+
* How a message's display uploads a file the person chose (REACTOR_UI
|
|
25
|
+
* RU-6): its ref, to press with. Absent, nothing is uploaded.
|
|
26
|
+
*/
|
|
27
|
+
upload?: UiUpload;
|
|
16
28
|
/** Whether the agent is loading */
|
|
17
29
|
isLoading: boolean;
|
|
18
30
|
/** Whether the agent is currently streaming */
|
|
@@ -97,5 +109,5 @@ export interface SubagentChatPanelProps {
|
|
|
97
109
|
* approval decisions are sent over the shared monitoring socket.
|
|
98
110
|
*/
|
|
99
111
|
export declare function SubagentChatPanel({ toolCallId, subagentName: subagentNameHint, height, avatarConfig, }: SubagentChatPanelProps): React.ReactElement | null;
|
|
100
|
-
export declare function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density, }: ChatMessageListProps): import("react").JSX.Element;
|
|
112
|
+
export declare function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density, }: ChatMessageListProps): import("react").JSX.Element;
|
|
101
113
|
export {};
|
|
@@ -12,8 +12,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
12
12
|
import { MicrophoneIcon } from '@datalayer/icons-react';
|
|
13
13
|
import { Fragment, lazy, Suspense, useState, useCallback, useEffect, useMemo, useRef, useSyncExternalStore, } from 'react';
|
|
14
14
|
import { useOptionalReactorPlatform } from '@datalayer/reactor/react';
|
|
15
|
-
import { resolveToolResult } from '@datalayer/reactor/ui';
|
|
16
|
-
import { ReactorDisplay } from '@datalayer/reactor/ui/react';
|
|
15
|
+
import { resolveToolResult, } from '@datalayer/reactor/ui';
|
|
16
|
+
import { DisplayText, ReactorDisplay } from '@datalayer/reactor/ui/react';
|
|
17
17
|
import { Text, Spinner, Link, Label, RelativeTime } from '@primer/react';
|
|
18
18
|
import { ArrowRightIcon, BroadcastIcon, ClockIcon, DependabotIcon, PersonIcon, SkipIcon, ToolsIcon, XCircleFillIcon, } from '@primer/octicons-react';
|
|
19
19
|
import { Box } from '@datalayer/primer-addons';
|
|
@@ -366,7 +366,7 @@ function DefaultToolCallRenderer({ item, onRespond, mcpServers, density, }) {
|
|
|
366
366
|
// ---------------------------------------------------------------------------
|
|
367
367
|
// Component
|
|
368
368
|
// ---------------------------------------------------------------------------
|
|
369
|
-
export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density = 'comfortable', }) {
|
|
369
|
+
export function ChatMessageList({ displayItems, actOn, upload, isLoading, isStreaming, showLoadingIndicator, hideMessagesAfterToolUI, avatarConfig, padding, notebookToolSurfacesId, emptyContent, messagesEndRef, onRespond, approvalConfig, showTurnFooters = false, agentUsage, onRemoveItems, renderMessageFooter, mcpServers, density = 'comfortable', }) {
|
|
370
370
|
/*
|
|
371
371
|
* Who draws a tool call: the Reactor plugins of the page, when it has a
|
|
372
372
|
* platform. Each plugin binds its own tools (`ToolBindings`) and renders its
|
|
@@ -611,7 +611,7 @@ export function ChatMessageList({ displayItems, isLoading, isStreaming, showLoad
|
|
|
611
611
|
whiteSpace: 'pre-wrap',
|
|
612
612
|
wordBreak: 'break-word',
|
|
613
613
|
}, children: [message.metadata?.input === 'voice' ? (_jsx(Box, { as: "span", "data-spoken": "", title: "Said aloud", "aria-label": "Said aloud:", display: "inline-flex", verticalAlign: "text-bottom", mr: 1, opacity: 0.85, children: _jsx(MicrophoneIcon, { size: 14, "aria-hidden": "true" }) })) : null, text] }));
|
|
614
|
-
})()) : message.live && !getMessageText(message) ? (_jsx(TypingDots, { size: 6 })) : (_jsx(ChatMarkdown, { density: density, text: getMessageText(message) || (isStreaming ? '...' : '') }))] }), renderMessageFooter ? (_jsx(Box, { display: "flex", gap: 2, alignItems: "center", mt: 1, flexWrap: "wrap", flexDirection: isUser ? 'row-reverse' : 'row', children: renderMessageFooter(message) })) : null] })] }) }, message.id));
|
|
614
|
+
})()) : message.live && !getMessageText(message) ? (_jsx(TypingDots, { size: 6 })) : (_jsx(ChatMarkdown, { density: density, text: getMessageText(message) || (isStreaming ? '...' : '') }))] }), message.displays?.length ? (_jsx(Box, { "data-chat-displays": "", display: "flex", flexDirection: "column", gap: 2, mt: 2, width: "100%", children: message.displays.map(display => platform ? (_jsx(ReactorDisplay, { display: display, act: actOn?.(display), upload: upload }, display.id)) : (_jsx(DisplayText, { display: display }, display.id))) })) : null, renderMessageFooter ? (_jsx(Box, { display: "flex", gap: 2, alignItems: "center", mt: 1, flexWrap: "wrap", flexDirection: isUser ? 'row-reverse' : 'row', children: renderMessageFooter(message) })) : null] })] }) }, message.id));
|
|
615
615
|
};
|
|
616
616
|
return (_jsxs(_Fragment, { children: [displayItems.map((item, index) => {
|
|
617
617
|
const itemUI = renderItem(item, index);
|
|
@@ -121,7 +121,7 @@ export declare function chartOption(points: Record<string, unknown>[], props: Pi
|
|
|
121
121
|
};
|
|
122
122
|
series: {
|
|
123
123
|
name: string;
|
|
124
|
-
type: "
|
|
124
|
+
type: "bar" | "line" | "scatter";
|
|
125
125
|
areaStyle: {} | undefined;
|
|
126
126
|
data: number[][] | (number | null)[];
|
|
127
127
|
}[];
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import { z } from 'zod/v3';
|
|
21
21
|
import { ActionSchema, DataBindingSchema } from '@a2ui/web_core/v0_9';
|
|
22
|
-
import { COMPONENT_CATALOGUE } from '../../../specs/
|
|
22
|
+
import { COMPONENT_CATALOGUE } from '../../../specs/ui';
|
|
23
23
|
/** The components of Datalayer's own the catalog has, by id. */
|
|
24
24
|
export const OWN_COMPONENT_IDS = Object.values(COMPONENT_CATALOGUE)
|
|
25
25
|
.filter(component => !component.standard)
|
|
@@ -30,8 +30,9 @@ export type AnswerPressed = {
|
|
|
30
30
|
does: string;
|
|
31
31
|
};
|
|
32
32
|
/**
|
|
33
|
-
* What a button pressed on
|
|
34
|
-
* action that is not one of
|
|
33
|
+
* What a button pressed on a member's surface sends back, or `null` for an
|
|
34
|
+
* action that is not one of its buttons (a form's submission). A member's
|
|
35
|
+
* surface is an answer by how it came — an A2A artifact — not by its id.
|
|
35
36
|
*/
|
|
36
37
|
export declare function pressedOf(action: {
|
|
37
38
|
name: string;
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Suspense, lazy } from 'react';
|
|
3
3
|
import { Text } from '@primer/react';
|
|
4
|
-
import { answerAction } from '../../apps/plugins/a2ui-surface/toolResult';
|
|
5
4
|
/** The renderer itself: A2UI and the catalog, loaded when a surface is drawn. */
|
|
6
5
|
const AnswerSurfacesView = lazy(() => import('./AnswerSurfacesView'));
|
|
7
6
|
/**
|
|
8
|
-
* What a button pressed on
|
|
9
|
-
* action that is not one of
|
|
7
|
+
* What a button pressed on a member's surface sends back, or `null` for an
|
|
8
|
+
* action that is not one of its buttons (a form's submission). A member's
|
|
9
|
+
* surface is an answer by how it came — an A2A artifact — not by its id.
|
|
10
10
|
*/
|
|
11
11
|
export function pressedOf(action) {
|
|
12
|
-
const
|
|
13
|
-
if (!
|
|
12
|
+
const name = action.name.trim();
|
|
13
|
+
if (!name || name === 'submit_a2ui_form') {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
const
|
|
16
|
+
const payload = { ...action.context };
|
|
17
17
|
const does = typeof payload.does === 'string' ? payload.does : 'read';
|
|
18
|
-
return { message:
|
|
18
|
+
return { message: name, action: { name, payload }, does };
|
|
19
19
|
}
|
|
20
20
|
/** The surfaces the members showed, each under who showed it; nothing when there is none. */
|
|
21
21
|
export function AnswerSurfaces(props) {
|
|
@@ -59,7 +59,7 @@ export declare const NOTEBOOK_AND_WORDS: readonly string[];
|
|
|
59
59
|
export declare const COMPONENTS_NOTEBOOK_AND_WORDS: readonly string[];
|
|
60
60
|
/** A surface of components a member showed with an answer: who gave it, and the surface. */
|
|
61
61
|
export type A2ATeamSurface = {
|
|
62
|
-
/** Its surface's id
|
|
62
|
+
/** Its surface's id. */
|
|
63
63
|
id: string;
|
|
64
64
|
/** The member that showed it, by its id. */
|
|
65
65
|
giver: string;
|