@datalayer/agent-runtimes 1.5.23 → 1.5.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist-embed/chunks/{CellSidebarExtension-CDnzN9Cb.js → CellSidebarExtension-B-73cmcQ.js} +1 -1
  2. package/dist-embed/chunks/{ChatView-BjZ993Ch.js → ChatView-CUWE6I5M.js} +3 -3
  3. package/dist-embed/chunks/{DeckComponent-Cb4_dZpN.js → DeckComponent-ymp4My5d.js} +1 -1
  4. package/dist-embed/chunks/{DocumentView-Caxb2xz-.js → DocumentView-i99p_Qzt.js} +1 -1
  5. package/dist-embed/chunks/{EphemeralDocument-DIHjR5l8.js → EphemeralDocument--pV47gPl.js} +1 -1
  6. package/dist-embed/chunks/{EphemeralNotebook-1Rwa3anx.js → EphemeralNotebook-BqccUXGd.js} +1 -1
  7. package/dist-embed/chunks/{ExcalidrawComponent-vJQwgcUt.js → ExcalidrawComponent-DgSMy796.js} +1 -1
  8. package/dist-embed/chunks/{ExcalidrawModal-BDgjdSz2.js → ExcalidrawModal-DFJW9pQh.js} +1 -1
  9. package/dist-embed/chunks/{JupyterLexicalExtension-_7FM5Om8.js → JupyterLexicalExtension-CqDatsgf.js} +5 -5
  10. package/dist-embed/chunks/{KernelVariables-CKIqy599.js → KernelVariables-CS6h5HyK.js} +1 -1
  11. package/dist-embed/chunks/{NotebookToolSurfaces-YAM1bvZS.js → NotebookToolSurfaces-CTLbTvsS.js} +1 -1
  12. package/dist-embed/chunks/{NotebookView-C8RCvsR1.js → NotebookView-BJpTApeL.js} +1 -1
  13. package/dist-embed/chunks/{Output-BWClePP-.js → Output-no9rnFJ7.js} +1 -1
  14. package/dist-embed/chunks/{SandboxDetailsDialog-CDSxIC0-.js → SandboxDetailsDialog-BAv6RMed.js} +1 -1
  15. package/dist-embed/chunks/{SchemaForm-CO_2yGg2.js → SchemaForm-CE5NfpBU.js} +1 -1
  16. package/dist-embed/chunks/{WidgetVariableView--osoOLv8.js → WidgetVariableView-V4DsgfIK.js} +1 -1
  17. package/dist-embed/chunks/{YouTubePlugin-BB7Z7INn.js → YouTubePlugin-CpgyVTwG.js} +1 -1
  18. package/dist-embed/chunks/{ar-SA-G6X2FPQ2-D6Ko4I2x.js → ar-SA-G6X2FPQ2-DwUjDWHp.js} +1 -1
  19. package/dist-embed/chunks/{az-AZ-76LH7QW2-B3UhxnSz.js → az-AZ-76LH7QW2-DROX4pJc.js} +1 -1
  20. package/dist-embed/chunks/{bg-BG-XCXSNQG7-BS-QRZjB.js → bg-BG-XCXSNQG7-DPLzP9Bm.js} +1 -1
  21. package/dist-embed/chunks/{bn-BD-2XOGV67Q-DNg0uIV0.js → bn-BD-2XOGV67Q-CCOU2J2j.js} +1 -1
  22. package/dist-embed/chunks/{ca-ES-6MX7JW3Y-Bn4yHfTY.js → ca-ES-6MX7JW3Y-3RgFapBd.js} +1 -1
  23. package/dist-embed/chunks/{cs-CZ-2BRQDIVT-DUGnyonZ.js → cs-CZ-2BRQDIVT-BJS7ofhJ.js} +1 -1
  24. package/dist-embed/chunks/{da-DK-5WZEPLOC-Is8nybqO.js → da-DK-5WZEPLOC-BHDT0zzh.js} +1 -1
  25. package/dist-embed/chunks/{de-DE-XR44H4JA-D54sMa4L.js → de-DE-XR44H4JA-CWVJKc9t.js} +1 -1
  26. package/dist-embed/chunks/{el-GR-BZB4AONW-CvM3SWat.js → el-GR-BZB4AONW-2z8DB9lI.js} +1 -1
  27. package/dist-embed/chunks/{es-ES-U4NZUMDT-BZDRRuRf.js → es-ES-U4NZUMDT-DzXGWPd4.js} +1 -1
  28. package/dist-embed/chunks/{eu-ES-A7QVB2H4-DAmknI9y.js → eu-ES-A7QVB2H4-CslHE4iE.js} +1 -1
  29. package/dist-embed/chunks/{fa-IR-HGAKTJCU-CQhKri4O.js → fa-IR-HGAKTJCU-CBWUHs2r.js} +1 -1
  30. package/dist-embed/chunks/{fi-FI-Z5N7JZ37-DzEDoP64.js → fi-FI-Z5N7JZ37-MAr8kZMI.js} +1 -1
  31. package/dist-embed/chunks/{fr-FR-RHASNOE6-DiSkyvEB.js → fr-FR-RHASNOE6-BUz3aHQJ.js} +1 -1
  32. package/dist-embed/chunks/{gl-ES-HMX3MZ6V-3AJMzY-I.js → gl-ES-HMX3MZ6V-DvzQmDYv.js} +1 -1
  33. package/dist-embed/chunks/{he-IL-6SHJWFNN-Cuh5an4X.js → he-IL-6SHJWFNN-DKR3cKyQ.js} +1 -1
  34. package/dist-embed/chunks/{hi-IN-IWLTKZ5I-BbeuFB83.js → hi-IN-IWLTKZ5I-C4tyBNwa.js} +1 -1
  35. package/dist-embed/chunks/{hu-HU-A5ZG7DT2-C1OMfhob.js → hu-HU-A5ZG7DT2-C8BnBgH5.js} +1 -1
  36. package/dist-embed/chunks/{id-ID-SAP4L64H-BzKILJTZ.js → id-ID-SAP4L64H-Bn1hY5OS.js} +1 -1
  37. package/dist-embed/chunks/{index-CPpm1tqJ.js → index-5N0EOEHV.js} +1 -1
  38. package/dist-embed/chunks/{index-f418CYn7.js → index-BKB97UwL.js} +1 -1
  39. package/dist-embed/chunks/{index-CyQgE_Es.js → index-BN8Bbau-.js} +2 -2
  40. package/dist-embed/chunks/{index-CKFrz1es.js → index-BTdq_niO.js} +1 -1
  41. package/dist-embed/chunks/{index-D4QM3qbA.js → index-Bhzj1Pnv.js} +1 -1
  42. package/dist-embed/chunks/{index-BMandbJB.js → index-BzMt3uYX.js} +1 -1
  43. package/dist-embed/chunks/{index-DLimRfHQ.js → index-CCRwowMu.js} +1 -1
  44. package/dist-embed/chunks/{index-BFdE0KFI.js → index-CEbnhuIo.js} +1 -1
  45. package/dist-embed/chunks/{index-CAd5YQQl.js → index-CZB8DyUw.js} +1 -1
  46. package/dist-embed/chunks/{index-BO6VQLH6.js → index-Cd5CgKFr.js} +1 -1
  47. package/dist-embed/chunks/{index-D64QvG43.js → index-CkQ6IAoY.js} +1 -1
  48. package/dist-embed/chunks/{index-BktUM85c.js → index-Coj9opLJ.js} +1 -1
  49. package/dist-embed/chunks/{index-dYwQAVs_.js → index-D9_026K2.js} +1 -1
  50. package/dist-embed/chunks/{index-SbFvdBPQ.js → index-DVaPKQyL.js} +1 -1
  51. package/dist-embed/chunks/{index-f9WZsG2n.js → index-DWfzdTZo.js} +1 -1
  52. package/dist-embed/chunks/{index-DY8mjz1l.js → index-DdBQBN76.js} +1 -1
  53. package/dist-embed/chunks/{index-DEjLPUAD.js → index-Di3aBGLV.js} +1 -1
  54. package/dist-embed/chunks/{index-Cu0bp_EI.js → index-Gngo0bXi.js} +1 -1
  55. package/dist-embed/chunks/{index-DyVR0igi.js → index-S4sn4R3i.js} +1 -1
  56. package/dist-embed/chunks/{index-noRoZrq5.js → index-Z2pxzg3H.js} +1 -1
  57. package/dist-embed/chunks/{index-PqsSImlj.js → index-_H5U6gal.js} +1 -1
  58. package/dist-embed/chunks/{index-x45cMPwN.js → index-_nsBY26B.js} +1 -1
  59. package/dist-embed/chunks/{index-CM7xo08K.js → index-b6e3y1R-.js} +1 -1
  60. package/dist-embed/chunks/{index-wk9vl2uW.js → index-szXn1pzS.js} +1 -1
  61. package/dist-embed/chunks/{index.esm-p0OnSU9h.js → index.esm-nZym6gpK.js} +1 -1
  62. package/dist-embed/chunks/isEqual-g-jK7vY0.js +1 -0
  63. package/dist-embed/chunks/{it-IT-JPQ66NNP-DuG05fUR.js → it-IT-JPQ66NNP-DEiaanA7.js} +1 -1
  64. package/dist-embed/chunks/{ja-JP-DBVTYXUO-DeDfL3Bk.js → ja-JP-DBVTYXUO-Bb0GBZrQ.js} +1 -1
  65. package/dist-embed/chunks/{kaa-6HZHGXH3-BRKFVG6s.js → kaa-6HZHGXH3-DxqTv9uN.js} +1 -1
  66. package/dist-embed/chunks/{kab-KAB-ZGHBKWFO-Uqakmhz5.js → kab-KAB-ZGHBKWFO-D0V0yQgV.js} +1 -1
  67. package/dist-embed/chunks/{kk-KZ-P5N5QNE5-DWMXMIAK.js → kk-KZ-P5N5QNE5-CYyEyHXW.js} +1 -1
  68. package/dist-embed/chunks/{km-KH-HSX4SM5Z-BXNl9QnV.js → km-KH-HSX4SM5Z-CCTEGikY.js} +1 -1
  69. package/dist-embed/chunks/{ko-KR-MTYHY66A-BZgIqTuN.js → ko-KR-MTYHY66A-C-QDut19.js} +1 -1
  70. package/dist-embed/chunks/{ku-TR-6OUDTVRD-CQNa5i5U.js → ku-TR-6OUDTVRD-CIqn8p2Y.js} +1 -1
  71. package/dist-embed/chunks/{lt-LT-XHIRWOB4-BQW2k7iv.js → lt-LT-XHIRWOB4-BDLUWFPH.js} +1 -1
  72. package/dist-embed/chunks/{lv-LV-5QDEKY6T-B0HRRCC4.js → lv-LV-5QDEKY6T-BItabIJP.js} +1 -1
  73. package/dist-embed/chunks/{mr-IN-CRQNXWMA-DXR6sWSH.js → mr-IN-CRQNXWMA-DXXb8N2w.js} +1 -1
  74. package/dist-embed/chunks/{my-MM-5M5IBNSE-BZ5_9oCm.js → my-MM-5M5IBNSE-TidIfjqS.js} +1 -1
  75. package/dist-embed/chunks/{nb-NO-T6EIAALU-CBtv36wZ.js → nb-NO-T6EIAALU-DEINynK7.js} +1 -1
  76. package/dist-embed/chunks/{nl-NL-IS3SIHDZ-CcqE3cY8.js → nl-NL-IS3SIHDZ-D04rJzNe.js} +1 -1
  77. package/dist-embed/chunks/{nn-NO-6E72VCQL-Cl9wPgpU.js → nn-NO-6E72VCQL-oWFzCtpf.js} +1 -1
  78. package/dist-embed/chunks/{oc-FR-POXYY2M6-CQEkV2p1.js → oc-FR-POXYY2M6-BmFfP6hk.js} +1 -1
  79. package/dist-embed/chunks/{pa-IN-N4M65BXN-CaE6yG0U.js → pa-IN-N4M65BXN-RkXrgKXw.js} +1 -1
  80. package/dist-embed/chunks/{percentages-BXMCSKIN-Bk5xifLL.js → percentages-BXMCSKIN-BL_DuU3S.js} +3 -3
  81. package/dist-embed/chunks/{pl-PL-T2D74RX3-M1pQcF2j.js → pl-PL-T2D74RX3-DqxkHzTj.js} +1 -1
  82. package/dist-embed/chunks/{pt-BR-5N22H2LF-BFDHu72z.js → pt-BR-5N22H2LF-DFA-wWC5.js} +1 -1
  83. package/dist-embed/chunks/{pt-PT-UZXXM6DQ-DFcZY1Gz.js → pt-PT-UZXXM6DQ-Z5jw_7aw.js} +1 -1
  84. package/dist-embed/chunks/{renderer-Dy4lZ1KZ.js → renderer-DKKfWkWv.js} +1 -1
  85. package/dist-embed/chunks/{ro-RO-JPDTUUEW-BGm_Xwik.js → ro-RO-JPDTUUEW-DjC09bHq.js} +1 -1
  86. package/dist-embed/chunks/{ru-RU-B4JR7IUQ-BsrZ_x8p.js → ru-RU-B4JR7IUQ-CqwD7Yik.js} +1 -1
  87. package/dist-embed/chunks/{si-LK-N5RQ5JYF-BfaYS44g.js → si-LK-N5RQ5JYF-OVf4k6G3.js} +1 -1
  88. package/dist-embed/chunks/{sk-SK-C5VTKIMK-D-K9yMTN.js → sk-SK-C5VTKIMK-BG__kjLT.js} +1 -1
  89. package/dist-embed/chunks/{sl-SI-NN7IZMDC-B48XvZUw.js → sl-SI-NN7IZMDC-CIgxidVH.js} +1 -1
  90. package/dist-embed/chunks/{subset-shared.chunk-DzA7mJjZ.js → subset-shared.chunk-BR0LJHtQ.js} +1 -1
  91. package/dist-embed/chunks/{subset-worker.chunk-CsTEdHAt.js → subset-worker.chunk-52aAcjvz.js} +1 -1
  92. package/dist-embed/chunks/{sv-SE-XGPEYMSR-DfhLO_Vt.js → sv-SE-XGPEYMSR-CYHz3xCa.js} +1 -1
  93. package/dist-embed/chunks/{ta-IN-2NMHFXQM-EZA_oxSt.js → ta-IN-2NMHFXQM-BphRr9QY.js} +1 -1
  94. package/dist-embed/chunks/{th-TH-HPSO5L25-G9vy_zQH.js → th-TH-HPSO5L25-Ba2-2Bo0.js} +1 -1
  95. package/dist-embed/chunks/{tr-TR-DEFEU3FU-DBj_G5CD.js → tr-TR-DEFEU3FU-CRKE7Roy.js} +1 -1
  96. package/dist-embed/chunks/{uk-UA-QMV73CPH-DN7lMu5H.js → uk-UA-QMV73CPH-DlNE5CAT.js} +1 -1
  97. package/dist-embed/chunks/{vi-VN-M7AON7JQ-LoT02qgd.js → vi-VN-M7AON7JQ-Cp44PRXY.js} +1 -1
  98. package/dist-embed/chunks/{zh-CN-LNUGB5OW-CjexwOqT.js → zh-CN-LNUGB5OW-JHqqwp0j.js} +1 -1
  99. package/dist-embed/chunks/{zh-HK-E62DVLB3-B0ys_HV6.js → zh-HK-E62DVLB3-YEs97mA8.js} +1 -1
  100. package/dist-embed/chunks/{zh-TW-RAJ6MFWO-tQJWLJuU.js → zh-TW-RAJ6MFWO-CgmeTbuy.js} +1 -1
  101. package/dist-embed/datalayer-app-main.js +65 -65
  102. package/lib/apps/plugins/pap-sign-in/signIn.d.ts +3 -89
  103. package/lib/apps/plugins/pap-sign-in/signIn.js +5 -142
  104. package/lib/chat/messages/ChatMessageList.js +3 -1
  105. package/lib/chat/presence/Presence.d.ts +4 -4
  106. package/lib/chat/presence/Presence.js +3 -2
  107. package/lib/chat/presence/presenceStatus.d.ts +3 -34
  108. package/lib/chat/presence/presenceStatus.js +4 -46
  109. package/lib/portable/conversation.d.ts +3 -0
  110. package/lib/portable/conversation.js +15 -1
  111. package/lib/portable/direct.d.ts +100 -0
  112. package/lib/portable/direct.js +172 -0
  113. package/lib/portable/displays.d.ts +109 -0
  114. package/lib/portable/displays.js +261 -0
  115. package/lib/portable/fixtures.d.ts +11 -0
  116. package/lib/portable/fixtures.js +72 -0
  117. package/lib/portable/index.d.ts +6 -0
  118. package/lib/portable/index.js +10 -0
  119. package/lib/portable/presence.d.ts +47 -0
  120. package/lib/portable/presence.js +71 -0
  121. package/lib/portable/signIn.d.ts +96 -0
  122. package/lib/portable/signIn.js +148 -0
  123. package/lib/portable/views.d.ts +147 -0
  124. package/lib/portable/views.js +52 -0
  125. package/lib/runtimes/__tests__/fixtures/accounting-agent-card.json +1 -1
  126. package/package.json +1 -1
  127. package/dist-embed/chunks/isEqual-Cwjr2skl.js +0 -1
@@ -0,0 +1,172 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /** The longest message the gateway takes. */
6
+ export const DIRECT_MAX = 4000;
7
+ /** What a page says around the person's own words. */
8
+ export const DIRECT_WORDS = {
9
+ notConfigured: 'Datalayer is not configured to send your words to a company here.',
10
+ signIn: 'Sign in to Datalayer first: only you write in your Direct Conversation.',
11
+ tooLong: `Write a message of at most ${DIRECT_MAX} characters.`,
12
+ empty: 'Write something to send.',
13
+ unread: (company) => `${company}'s answer could not be read.`,
14
+ write: (company) => `Write to ${company} yourself`,
15
+ says: (company) => `Your own words to ${company}, sent as yours, not through the application`,
16
+ writing: (company) => `Writing to ${company} yourself`,
17
+ how: 'What you write here goes to the company as your own words, never through the application. The company’s answers appear here.',
18
+ you: 'You',
19
+ agent: 'Its agent',
20
+ human: 'One of its people',
21
+ back: 'Back to the application',
22
+ closed: (company) => `${company} closed this conversation: write through the application again.`,
23
+ locked: (company) => `You are writing to ${company} yourself: go back to the application to talk to it.`,
24
+ };
25
+ const root = (url) => url.replace(/\/+$/, '');
26
+ /** The sentence a refusal carries: its `note` (closed, none, another) or its `detail`. */
27
+ async function refusal(response, fallback) {
28
+ const body = (await response.json().catch(() => ({})));
29
+ const said = typeof body.note === 'string'
30
+ ? body.note
31
+ : typeof body.detail === 'string'
32
+ ? body.detail
33
+ : '';
34
+ return new Error(said || `${fallback} (${response.status})`);
35
+ }
36
+ /** The gateway's answer, read whole or refused. */
37
+ export function directAnswerOf(raw, company) {
38
+ const data = raw;
39
+ const replies = data?.replies;
40
+ if (!data ||
41
+ typeof data.direct_conversation !== 'string' ||
42
+ !Array.isArray(replies) ||
43
+ typeof data.status !== 'string' ||
44
+ replies.some(reply => !reply ||
45
+ typeof reply.text !== 'string' ||
46
+ (reply.from !== 'agent' &&
47
+ reply.from !== 'human'))) {
48
+ throw new Error(DIRECT_WORDS.unread(company));
49
+ }
50
+ return {
51
+ directConversation: data.direct_conversation,
52
+ replies: replies.map(reply => ({
53
+ text: reply.text,
54
+ from: reply.from,
55
+ })),
56
+ status: data.status,
57
+ responder: typeof data.responder === 'string' ? data.responder : '',
58
+ cursor: typeof data.cursor === 'string' ? data.cursor : '',
59
+ display: data.display ?? null,
60
+ };
61
+ }
62
+ /** IAM's ticket and the gateway's Direct Conversation routes, as the person. */
63
+ export function directClient({ iamUrl, gatewayUrl, getAccessToken, fetch: fetcher, }) {
64
+ const bearer = () => {
65
+ const token = getAccessToken();
66
+ if (!token) {
67
+ throw new Error(DIRECT_WORDS.signIn);
68
+ }
69
+ return { Authorization: `Bearer ${token}` };
70
+ };
71
+ const configured = (...urls) => {
72
+ if (urls.some(url => !url)) {
73
+ throw new Error(DIRECT_WORDS.notConfigured);
74
+ }
75
+ };
76
+ /** A one-use ticket for one message, asked of IAM with the person's own sign-in. */
77
+ const ticket = async (target) => {
78
+ configured(iamUrl);
79
+ const response = await fetcher(`${root(iamUrl)}/api/iam/v1/pap/my/direct`, {
80
+ method: 'POST',
81
+ headers: { ...bearer(), 'Content-Type': 'application/json' },
82
+ body: JSON.stringify({
83
+ app_uid: target.appUid,
84
+ company: target.company,
85
+ conversation_id: target.conversationId,
86
+ }),
87
+ });
88
+ if (!response.ok) {
89
+ throw await refusal(response, 'Your words could not be sent');
90
+ }
91
+ const body = (await response.json());
92
+ if (typeof body.ticket !== 'string' || !body.ticket) {
93
+ throw new Error('Your words could not be sent: no ticket came back.');
94
+ }
95
+ return body.ticket;
96
+ };
97
+ return {
98
+ ticket,
99
+ /**
100
+ * The person's own words, sent as theirs: a ticket, then the words with
101
+ * it. Refused in the gateway's sentence, and never retried.
102
+ */
103
+ send: async (target, text) => {
104
+ const words = text.trim();
105
+ if (!words) {
106
+ throw new Error(DIRECT_WORDS.empty);
107
+ }
108
+ if (words.length > DIRECT_MAX) {
109
+ throw new Error(DIRECT_WORDS.tooLong);
110
+ }
111
+ configured(gatewayUrl);
112
+ const used = await ticket(target);
113
+ const response = await fetcher(`${root(gatewayUrl)}/api/mcp/v1/pap/direct`, {
114
+ method: 'POST',
115
+ headers: { ...bearer(), 'Content-Type': 'application/json' },
116
+ body: JSON.stringify({ ticket: used, text: words }),
117
+ });
118
+ if (!response.ok) {
119
+ throw await refusal(response, 'Your words were not sent');
120
+ }
121
+ return directAnswerOf(await response.json(), target.company);
122
+ },
123
+ /** What the company wrote since `cursor`: read, never sent again. */
124
+ read: async (target, cursor = '') => {
125
+ configured(gatewayUrl);
126
+ const query = new URLSearchParams({
127
+ app_uid: target.appUid,
128
+ company: target.company,
129
+ conversation_id: target.conversationId,
130
+ });
131
+ if (cursor) {
132
+ query.set('cursor', cursor);
133
+ }
134
+ const response = await fetcher(`${root(gatewayUrl)}/api/mcp/v1/pap/direct?${query.toString()}`, { headers: bearer() });
135
+ if (!response.ok) {
136
+ throw await refusal(response, DIRECT_WORDS.unread(target.company));
137
+ }
138
+ return directAnswerOf(await response.json(), target.company);
139
+ },
140
+ };
141
+ }
142
+ /** A Direct Conversation opened under the conversation a company answered in. */
143
+ export const openDirect = (target) => ({
144
+ target,
145
+ lines: [],
146
+ cursor: '',
147
+ closed: false,
148
+ });
149
+ /** The open conversation with what the company answered, and the person's words when they sent some. */
150
+ export function withDirectAnswer(open, answer, mine) {
151
+ return {
152
+ target: {
153
+ ...open.target,
154
+ conversationId: answer.directConversation || open.target.conversationId,
155
+ },
156
+ lines: [
157
+ ...open.lines,
158
+ ...(mine !== undefined
159
+ ? [{ from: 'you', text: mine.trim() }]
160
+ : []),
161
+ ...answer.replies.map(reply => ({ from: reply.from, text: reply.text })),
162
+ ],
163
+ cursor: answer.cursor || open.cursor,
164
+ closed: answer.status === 'closed',
165
+ };
166
+ }
167
+ /** Who wrote a line, said apart: the person, the company's agent, one of its people. */
168
+ export const directAuthor = (line) => line.from === 'you'
169
+ ? DIRECT_WORDS.you
170
+ : line.from === 'agent'
171
+ ? DIRECT_WORDS.agent
172
+ : DIRECT_WORDS.human;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * The protocol displays a run's tool results carry, read strictly for any
3
+ * renderer (MOBILE M-037, REACTOR_UI RU-1): the page's half of a tool result
4
+ * — `_meta["reactor.ui/display"]`, as the runtime's AG-UI stream sends it
5
+ * beside the model's view — decoded into what a card draws.
6
+ *
7
+ * - **Sign in at a company** (`…pap.sign-in+json`, and a plain `pap_sign_in`
8
+ * or a business's `pact_sign_in` answer): Direct, Device or Mediated, its
9
+ * page (`https` only), its code, until when, what it needs.
10
+ * - **An operation to confirm** (`…pap.confirm+json`): the company's words
11
+ * verbatim, its terms, the revision and the one it replaced, what it does
12
+ * (a Rules class; `buy` when money moves — a purchase), whether only the
13
+ * person may decide. It is answered where approvals are, never here: the
14
+ * approval it stands for is named (`approvalId`).
15
+ * - **What a confirmed operation came to** (`…pap.outcome+json`).
16
+ * - **What a company answered** (`…pap.replies+json`): each reply with who
17
+ * wrote it there (its agent or one of its people), who answers now and
18
+ * where the conversation stands — participation.
19
+ * - **A handoff** (`pap_handoff`'s answer): handed to the company's people,
20
+ * or why not.
21
+ *
22
+ * Anything else — an unknown type, a known one whose data does not read — is
23
+ * `unknown`: its type, version and model-safe text, nothing drawn from its
24
+ * data (diagnosable and safe). Typed decoding fails closed.
25
+ *
26
+ * @module portable/displays
27
+ */
28
+ import { type PapSignInWaiting } from './signIn';
29
+ export declare const PAP_SIGN_IN_TYPE = "application/vnd.datalayer.pap.sign-in+json";
30
+ export declare const PAP_CONFIRM_TYPE = "application/vnd.datalayer.pap.confirm+json";
31
+ export declare const PAP_OUTCOME_TYPE = "application/vnd.datalayer.pap.outcome+json";
32
+ export declare const PAP_REPLIES_TYPE = "application/vnd.datalayer.pap.replies+json";
33
+ /** Where a tool result carries its display for the page. */
34
+ export declare const DISPLAY_META_KEY = "reactor.ui/display";
35
+ /** What confirming an operation does, as an application's Rules class it. */
36
+ export type ActionClass = 'read' | 'write' | 'send' | 'buy' | 'delete' | 'publish';
37
+ export type ProtocolDisplay = ({
38
+ kind: 'sign-in';
39
+ id: string;
40
+ } & PapSignInWaiting) | {
41
+ kind: 'sign-in-refused';
42
+ id: string;
43
+ why: string;
44
+ } | {
45
+ kind: 'confirm';
46
+ id: string;
47
+ /** The approval it stands for: answered where approvals are. */
48
+ approvalId: string;
49
+ company: string;
50
+ summary: string;
51
+ terms: Record<string, unknown>;
52
+ operationRef: string;
53
+ revision: number;
54
+ does: ActionClass | null;
55
+ signedIn: boolean | null;
56
+ expiresAt: string;
57
+ was: {
58
+ revision: number;
59
+ terms: Record<string, unknown>;
60
+ } | null;
61
+ onlyThePerson: boolean;
62
+ } | {
63
+ kind: 'outcome';
64
+ id: string;
65
+ company: string;
66
+ summary: string;
67
+ operationRef: string;
68
+ revision: number;
69
+ state: 'in_progress' | 'succeeded' | 'failed' | 'cancelled' | 'expired';
70
+ result: string;
71
+ approvedBy: 'user' | 'standing_permission' | null;
72
+ } | {
73
+ kind: 'replies';
74
+ id: string;
75
+ company: string;
76
+ conversation: string;
77
+ replies: {
78
+ sender: 'agent' | 'human';
79
+ text: string;
80
+ }[];
81
+ status: 'working' | 'idle' | 'queued' | 'closed' | null;
82
+ responder: 'agent' | 'human' | null;
83
+ } | {
84
+ kind: 'handoff';
85
+ id: string;
86
+ handed: boolean;
87
+ responder: 'agent' | 'human' | null;
88
+ note: string;
89
+ } | {
90
+ kind: 'unknown';
91
+ id: string;
92
+ type: string;
93
+ version: number;
94
+ text: string;
95
+ };
96
+ /** A display's wire (`{type, version, id, text, data}`) decoded, or `unknown` with its text. */
97
+ export declare function protocolDisplayOf(wire: unknown): ProtocolDisplay | null;
98
+ /** `pap_handoff`, as the gateway names it with or without a prefix. */
99
+ export declare const PAP_HANDOFF_TOOL: RegExp;
100
+ /**
101
+ * The protocol display of a tool's result, or `null` for one that has none:
102
+ * its embedded display first; else a plain `pap_sign_in`/`pact_sign_in`
103
+ * answer or a `pap_handoff` answer, read by the tool's name.
104
+ */
105
+ export declare function displayOfToolResult(toolName: string, result: unknown, id?: string): ProtocolDisplay | null;
106
+ /** What each display says in one line, for a screen reader and a collapsed row. */
107
+ export declare function displayLine(display: ProtocolDisplay): string;
108
+ /** Who wrote a reply, said apart from the person's words and the agent's summaries (PAP-7.8-01). */
109
+ export declare const replyAuthor: (company: string, sender: "agent" | "human") => string;
@@ -0,0 +1,261 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The protocol displays a run's tool results carry, read strictly for any
7
+ * renderer (MOBILE M-037, REACTOR_UI RU-1): the page's half of a tool result
8
+ * — `_meta["reactor.ui/display"]`, as the runtime's AG-UI stream sends it
9
+ * beside the model's view — decoded into what a card draws.
10
+ *
11
+ * - **Sign in at a company** (`…pap.sign-in+json`, and a plain `pap_sign_in`
12
+ * or a business's `pact_sign_in` answer): Direct, Device or Mediated, its
13
+ * page (`https` only), its code, until when, what it needs.
14
+ * - **An operation to confirm** (`…pap.confirm+json`): the company's words
15
+ * verbatim, its terms, the revision and the one it replaced, what it does
16
+ * (a Rules class; `buy` when money moves — a purchase), whether only the
17
+ * person may decide. It is answered where approvals are, never here: the
18
+ * approval it stands for is named (`approvalId`).
19
+ * - **What a confirmed operation came to** (`…pap.outcome+json`).
20
+ * - **What a company answered** (`…pap.replies+json`): each reply with who
21
+ * wrote it there (its agent or one of its people), who answers now and
22
+ * where the conversation stands — participation.
23
+ * - **A handoff** (`pap_handoff`'s answer): handed to the company's people,
24
+ * or why not.
25
+ *
26
+ * Anything else — an unknown type, a known one whose data does not read — is
27
+ * `unknown`: its type, version and model-safe text, nothing drawn from its
28
+ * data (diagnosable and safe). Typed decoding fails closed.
29
+ *
30
+ * @module portable/displays
31
+ */
32
+ import { isPapSignInTool, readPapSignIn, } from './signIn';
33
+ export const PAP_SIGN_IN_TYPE = 'application/vnd.datalayer.pap.sign-in+json';
34
+ export const PAP_CONFIRM_TYPE = 'application/vnd.datalayer.pap.confirm+json';
35
+ export const PAP_OUTCOME_TYPE = 'application/vnd.datalayer.pap.outcome+json';
36
+ export const PAP_REPLIES_TYPE = 'application/vnd.datalayer.pap.replies+json';
37
+ /** Where a tool result carries its display for the page. */
38
+ export const DISPLAY_META_KEY = 'reactor.ui/display';
39
+ const record = (value) => value && typeof value === 'object' && !Array.isArray(value)
40
+ ? value
41
+ : null;
42
+ const text = (value) => typeof value === 'string' ? value : '';
43
+ const words = (value) => text(value).trim();
44
+ const count = (value) => typeof value === 'number' && Number.isInteger(value) && value >= 1
45
+ ? value
46
+ : 0;
47
+ const ACTIONS = [
48
+ 'read',
49
+ 'write',
50
+ 'send',
51
+ 'buy',
52
+ 'delete',
53
+ 'publish',
54
+ ];
55
+ const OUTCOMES = [
56
+ 'in_progress',
57
+ 'succeeded',
58
+ 'failed',
59
+ 'cancelled',
60
+ 'expired',
61
+ ];
62
+ const STATUSES = ['working', 'idle', 'queued', 'closed'];
63
+ const who = (value) => value === 'agent' || value === 'human' ? value : null;
64
+ /** A display's wire (`{type, version, id, text, data}`) decoded, or `unknown` with its text. */
65
+ export function protocolDisplayOf(wire) {
66
+ const shown = record(wire);
67
+ if (!shown || !text(shown.type)) {
68
+ return null;
69
+ }
70
+ const id = text(shown.id);
71
+ const data = record(shown.data) ?? {};
72
+ const unknown = {
73
+ kind: 'unknown',
74
+ id,
75
+ type: text(shown.type),
76
+ version: typeof shown.version === 'number' ? shown.version : 0,
77
+ text: text(shown.text),
78
+ };
79
+ switch (shown.type) {
80
+ case PAP_SIGN_IN_TYPE: {
81
+ const read = readPapSignIn({ status: 'waiting_for_the_person', ...data });
82
+ if (!read)
83
+ return unknown;
84
+ return 'refused' in read
85
+ ? { kind: 'sign-in-refused', id, why: read.refused }
86
+ : { kind: 'sign-in', id, ...read };
87
+ }
88
+ case PAP_CONFIRM_TYPE: {
89
+ const company = words(data.company);
90
+ const summary = text(data.summary);
91
+ const operationRef = words(data.operation_ref);
92
+ const revision = count(data.revision);
93
+ const terms = record(data.terms);
94
+ const was = record(data.was);
95
+ const wasRevision = was ? count(was.revision) : 0;
96
+ if (!company ||
97
+ !summary.trim() ||
98
+ !operationRef ||
99
+ !revision ||
100
+ !terms ||
101
+ !id.endsWith('-confirm')) {
102
+ return unknown;
103
+ }
104
+ if (was && (!wasRevision || wasRevision >= revision)) {
105
+ return unknown;
106
+ }
107
+ const does = data.does === null || data.does === undefined
108
+ ? null
109
+ : ACTIONS.find(a => a === data.does);
110
+ if (does === undefined)
111
+ return unknown;
112
+ const session = record(data.session);
113
+ return {
114
+ kind: 'confirm',
115
+ id,
116
+ approvalId: id.slice(0, -'-confirm'.length),
117
+ company,
118
+ summary,
119
+ terms,
120
+ operationRef,
121
+ revision,
122
+ does,
123
+ signedIn: session && typeof session.signed_in === 'boolean'
124
+ ? session.signed_in
125
+ : null,
126
+ expiresAt: text(data.expires_at),
127
+ was: was
128
+ ? { revision: wasRevision, terms: record(was.terms) ?? {} }
129
+ : null,
130
+ onlyThePerson: data.only_the_person === true,
131
+ };
132
+ }
133
+ case PAP_OUTCOME_TYPE: {
134
+ const state = OUTCOMES.find(s => s === data.state);
135
+ const company = words(data.company);
136
+ const summary = text(data.summary);
137
+ const result = text(data.result);
138
+ if (!state ||
139
+ !company ||
140
+ !summary.trim() ||
141
+ !words(data.operation_ref) ||
142
+ !count(data.revision)) {
143
+ return unknown;
144
+ }
145
+ // Final, it says what it came to in the company's words; in progress, nothing yet.
146
+ if ((state === 'in_progress') !== !result.trim())
147
+ return unknown;
148
+ return {
149
+ kind: 'outcome',
150
+ id,
151
+ company,
152
+ summary,
153
+ operationRef: words(data.operation_ref),
154
+ revision: count(data.revision),
155
+ state,
156
+ result,
157
+ approvedBy: data.approved_by === 'user' ||
158
+ data.approved_by === 'standing_permission'
159
+ ? data.approved_by
160
+ : null,
161
+ };
162
+ }
163
+ case PAP_REPLIES_TYPE: {
164
+ const company = words(data.company);
165
+ const conversation = text(data.conversation);
166
+ const replies = Array.isArray(data.replies)
167
+ ? data.replies.map(record).map(reply => ({
168
+ sender: who(reply?.sender),
169
+ text: text(reply?.text),
170
+ }))
171
+ : [];
172
+ if (!company ||
173
+ !/^[A-Za-z0-9_-]{1,256}$/.test(conversation) ||
174
+ !replies.length ||
175
+ replies.some(reply => !reply.sender || !reply.text.trim())) {
176
+ return unknown;
177
+ }
178
+ return {
179
+ kind: 'replies',
180
+ id,
181
+ company,
182
+ conversation,
183
+ replies: replies,
184
+ status: STATUSES.find(s => s === data.status) ?? null,
185
+ responder: who(data.responder),
186
+ };
187
+ }
188
+ default:
189
+ return unknown;
190
+ }
191
+ }
192
+ /** `pap_handoff`, as the gateway names it with or without a prefix. */
193
+ export const PAP_HANDOFF_TOOL = /(^|[_.:\-/])pap_handoff$/;
194
+ /**
195
+ * The protocol display of a tool's result, or `null` for one that has none:
196
+ * its embedded display first; else a plain `pap_sign_in`/`pact_sign_in`
197
+ * answer or a `pap_handoff` answer, read by the tool's name.
198
+ */
199
+ export function displayOfToolResult(toolName, result, id = '') {
200
+ const answer = record(result);
201
+ const embedded = record(answer?._meta)?.[DISPLAY_META_KEY];
202
+ if (embedded !== undefined) {
203
+ return protocolDisplayOf(embedded);
204
+ }
205
+ if (isPapSignInTool(toolName)) {
206
+ const read = readPapSignIn(result);
207
+ if (!read)
208
+ return null;
209
+ return 'refused' in read
210
+ ? { kind: 'sign-in-refused', id, why: read.refused }
211
+ : { kind: 'sign-in', id, ...read };
212
+ }
213
+ if (PAP_HANDOFF_TOOL.test(toolName) && answer) {
214
+ const status = text(answer.status);
215
+ if (!status)
216
+ return null;
217
+ return {
218
+ kind: 'handoff',
219
+ id,
220
+ handed: status !== 'closed' && status !== 'direct_conversation_open',
221
+ responder: who(answer.responder),
222
+ note: text(answer.note),
223
+ };
224
+ }
225
+ return null;
226
+ }
227
+ /** What each display says in one line, for a screen reader and a collapsed row. */
228
+ export function displayLine(display) {
229
+ switch (display.kind) {
230
+ case 'sign-in':
231
+ return display.mode === 'mediated'
232
+ ? `Sign in through ${display.host}`
233
+ : `Sign in at ${display.company || display.host}`;
234
+ case 'sign-in-refused':
235
+ return display.why;
236
+ case 'confirm':
237
+ return `${display.company} asks you to confirm: ${display.summary} (version ${display.revision})`;
238
+ case 'outcome':
239
+ return display.state === 'in_progress'
240
+ ? `Still in progress at ${display.company}: ${display.summary}`
241
+ : `${OUTCOME_LEADS[display.state]} ${display.company}: ${display.result}`;
242
+ case 'replies':
243
+ return `${display.company} answered${display.responder === 'human' ? ': one of its people is answering' : ''}`;
244
+ case 'handoff':
245
+ return display.handed
246
+ ? `Handed to ${display.responder === 'human' ? 'one of the company’s people' : 'the company'}`
247
+ : display.note || 'Nothing was handed over.';
248
+ case 'unknown':
249
+ return display.text || `A display this app cannot draw (${display.type})`;
250
+ }
251
+ }
252
+ const OUTCOME_LEADS = {
253
+ succeeded: 'Done at',
254
+ failed: 'Failed at',
255
+ cancelled: 'Cancelled at',
256
+ expired: 'Expired at',
257
+ };
258
+ /** Who wrote a reply, said apart from the person's words and the agent's summaries (PAP-7.8-01). */
259
+ export const replyAuthor = (company, sender) => sender === 'human'
260
+ ? `${company}, one of its people`
261
+ : `${company}, its agent`;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * A conversation with one row of every kind, and a question waiting: the
3
+ * fixture every renderer of `portable/views` is tested against — the web's
4
+ * and Datalayer Mobile's (MOBILE §4.2) — so that a change of a row's words or
5
+ * states is caught once.
6
+ *
7
+ * @module portable/fixtures
8
+ */
9
+ import { type ConversationState } from './conversation';
10
+ /** A fresh copy of the fixture conversation. */
11
+ export declare function conversationFixture(): ConversationState;
@@ -0,0 +1,72 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * A conversation with one row of every kind, and a question waiting: the
7
+ * fixture every renderer of `portable/views` is tested against — the web's
8
+ * and Datalayer Mobile's (MOBILE §4.2) — so that a change of a row's words or
9
+ * states is caught once.
10
+ *
11
+ * @module portable/fixtures
12
+ */
13
+ import { emptyConversation } from './conversation';
14
+ import { RUN_END_WORDS } from './records';
15
+ /** A fresh copy of the fixture conversation. */
16
+ export function conversationFixture() {
17
+ return {
18
+ ...emptyConversation(),
19
+ items: [
20
+ {
21
+ kind: 'message',
22
+ id: 'm-1',
23
+ role: 'user',
24
+ text: 'Where is order 7?',
25
+ streaming: false,
26
+ },
27
+ {
28
+ kind: 'tool',
29
+ id: 't-1',
30
+ name: 'lookup_order',
31
+ args: '{"order":7}',
32
+ status: 'complete',
33
+ result: 'Shipped on Monday.',
34
+ error: '',
35
+ display: null,
36
+ },
37
+ {
38
+ kind: 'tool',
39
+ id: 't-2',
40
+ name: 'send_mail',
41
+ args: '{}',
42
+ status: 'error',
43
+ result: '',
44
+ error: 'Refused by its rule.',
45
+ display: null,
46
+ },
47
+ {
48
+ kind: 'step',
49
+ id: 's-1',
50
+ name: 'Check the carrier',
51
+ status: 'complete',
52
+ },
53
+ {
54
+ kind: 'message',
55
+ id: 'm-2',
56
+ role: 'assistant',
57
+ text: 'It **shipped** on Monday.',
58
+ streaming: false,
59
+ },
60
+ { kind: 'end', id: 'e-1', end: 'stopped', words: RUN_END_WORDS.stopped },
61
+ { kind: 'error', id: 'x-1', text: 'The runtime did not answer.' },
62
+ ],
63
+ asking: {
64
+ kind: 'choice',
65
+ prompt: 'Track it by mail?',
66
+ options: ['Yes', 'No'],
67
+ schema: null,
68
+ accept: [],
69
+ },
70
+ dropped: 3,
71
+ };
72
+ }
@@ -12,5 +12,11 @@ export { AG_UI_NOT_ALLOWED, AG_UI_NOT_ON_RUNTIME, agUiRefusal, refusalBody, refu
12
12
  export { ASKED_FOR_TAG, askedOf, RUN_END_WORDS } from './records';
13
13
  export { messagesOfThread, openThread, snapshotOfStream, THREAD_WORDS, } from './thread';
14
14
  export { CUT_MARK, DIAGNOSTIC_LIMIT, emptyConversation, formFieldsOf, ITEM_LIMIT, MESSAGE_TEXT_LIMIT, questionOf, questionWords, reduceConversation, TOOL_TEXT_LIMIT, withError, withPrompt, withTurnEnded, type ConversationItem, type ConversationQuestion, type ConversationState, type TurnEnd, } from './conversation';
15
+ export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf, type PresenceState, type PresenceTool, } from './presence';
16
+ export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, type ComposerProps, type ComposerState, type ConversationViewProps, type ErrorItem, type ErrorRowProps, type MessageItem, type MessageRowProps, type PresenceLineProps, type ProtocolDisplayProps, type QuestionCardProps, type StepItem, type StepRowProps, type ToolItem, type ToolRowProps, type TurnEndItem, type TurnEndRowProps, } from './views';
17
+ export { conversationFixture } from './fixtures';
18
+ export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor, type ActionClass, type ProtocolDisplay, } from './displays';
19
+ export { isPapSignInTool, PAP_SIGN_IN_WORDS, readPapSignIn, safeSignInUrl, type PapSignInWaiting, } from './signIn';
20
+ export { DIRECT_MAX, DIRECT_WORDS, directAnswerOf, directAuthor, directClient, openDirect, withDirectAnswer, type DirectAnswer, type DirectClient, type DirectClientOptions, type DirectLine, type DirectOpen, type DirectReply, type DirectTarget, } from './direct';
15
21
  export { agentRunClient, agentRuntimesUrlOf, appSessionsClient, RuntimeRefusal, type AgentRunClient, type AgentTurn, type AppSessionsClient, type RuntimeClientOptions, type SessionStart, type StreamOptions, } from './sessions';
16
22
  export type { ChatMessage } from '../types/messages';
@@ -19,5 +19,15 @@ export { ASKED_FOR_TAG, askedOf, RUN_END_WORDS } from './records';
19
19
  export { messagesOfThread, openThread, snapshotOfStream, THREAD_WORDS, } from './thread';
20
20
  // A conversation as its stream builds it, for a native client.
21
21
  export { CUT_MARK, DIAGNOSTIC_LIMIT, emptyConversation, formFieldsOf, ITEM_LIMIT, MESSAGE_TEXT_LIMIT, questionOf, questionWords, reduceConversation, TOOL_TEXT_LIMIT, withError, withPrompt, withTurnEnded, } from './conversation';
22
+ // What an application is doing, in the words beside its face (LOOP T-08).
23
+ export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf, } from './presence';
24
+ // One contract for every renderer of a conversation's components.
25
+ export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, } from './views';
26
+ export { conversationFixture } from './fixtures';
27
+ // The protocol displays a run's tool results carry (MOBILE M-037).
28
+ export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor, } from './displays';
29
+ export { isPapSignInTool, PAP_SIGN_IN_WORDS, readPapSignIn, safeSignInUrl, } from './signIn';
30
+ // A Direct Conversation the person types in themselves (PAP_DATALAYER P-08, MOBILE M-038).
31
+ export { DIRECT_MAX, DIRECT_WORDS, directAnswerOf, directAuthor, directClient, openDirect, withDirectAnswer, } from './direct';
22
32
  // The runtime's routes a native client drives.
23
33
  export { agentRunClient, agentRuntimesUrlOf, appSessionsClient, RuntimeRefusal, } from './sessions';