@datalayer/agent-runtimes 1.5.83 → 1.5.85

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 (88) hide show
  1. package/dist-embed/assets/loro_wasm_bg-XW642dR7.wasm +0 -0
  2. package/dist-embed/chunks/{DeckComponent-9fShp5EH.js → DeckComponent-YdJOxQXT.js} +1 -1
  3. package/dist-embed/chunks/{DocumentView-DKhM55kg.js → DocumentView-DM-hlTc_.js} +1 -1
  4. package/dist-embed/chunks/{EphemeralDocument-C0_-sZg-.js → EphemeralDocument-B72SGhNu.js} +1 -1
  5. package/dist-embed/chunks/{ExcalidrawComponent-B5qoSnHE.js → ExcalidrawComponent-DdlI5Tll.js} +1 -1
  6. package/dist-embed/chunks/{ExcalidrawModal-DLDzJz7F.js → ExcalidrawModal-DbGpgFBD.js} +1 -1
  7. package/dist-embed/chunks/{JupyterLexicalExtension-2AkqHLoO.js → JupyterLexicalExtension-BXeUUzz7.js} +46 -46
  8. package/dist-embed/chunks/{YouTubePlugin-VNlkEtVU.js → YouTubePlugin-BZneOR95.js} +1 -1
  9. package/dist-embed/chunks/{ar-SA-G6X2FPQ2-Cqss6Q62.js → ar-SA-G6X2FPQ2-BamUG1DS.js} +1 -1
  10. package/dist-embed/chunks/{az-AZ-76LH7QW2-lD6kXi4J.js → az-AZ-76LH7QW2-Dj8Unmrc.js} +1 -1
  11. package/dist-embed/chunks/{bg-BG-XCXSNQG7-2BArB_Ch.js → bg-BG-XCXSNQG7-D5eDDJTP.js} +1 -1
  12. package/dist-embed/chunks/{bn-BD-2XOGV67Q-CBEeK0DN.js → bn-BD-2XOGV67Q-BtdKy5Qj.js} +1 -1
  13. package/dist-embed/chunks/{ca-ES-6MX7JW3Y-DmhCP7qy.js → ca-ES-6MX7JW3Y-B1_G67dI.js} +1 -1
  14. package/dist-embed/chunks/{cs-CZ-2BRQDIVT-jdPxvKmz.js → cs-CZ-2BRQDIVT-cvUFGqN3.js} +1 -1
  15. package/dist-embed/chunks/{da-DK-5WZEPLOC-vjNwrU4T.js → da-DK-5WZEPLOC-B0GASd9Y.js} +1 -1
  16. package/dist-embed/chunks/{de-DE-XR44H4JA-B6zQ6e3N.js → de-DE-XR44H4JA-BXX-hBxk.js} +1 -1
  17. package/dist-embed/chunks/{el-GR-BZB4AONW-CBhimaPi.js → el-GR-BZB4AONW-CmOubvn_.js} +1 -1
  18. package/dist-embed/chunks/{es-ES-U4NZUMDT-COAfPMQS.js → es-ES-U4NZUMDT-CUXrz6y_.js} +1 -1
  19. package/dist-embed/chunks/{eu-ES-A7QVB2H4-CiYUAKuY.js → eu-ES-A7QVB2H4-UidDpFzE.js} +1 -1
  20. package/dist-embed/chunks/{fa-IR-HGAKTJCU-CW2b0s1I.js → fa-IR-HGAKTJCU-c14o5fY5.js} +1 -1
  21. package/dist-embed/chunks/{fi-FI-Z5N7JZ37-MCVKKeJl.js → fi-FI-Z5N7JZ37-C2N4F3P9.js} +1 -1
  22. package/dist-embed/chunks/{fr-FR-RHASNOE6-DZMkwkhL.js → fr-FR-RHASNOE6-Docsp2bQ.js} +1 -1
  23. package/dist-embed/chunks/{gl-ES-HMX3MZ6V-D8LdswtD.js → gl-ES-HMX3MZ6V-ChUFEvG4.js} +1 -1
  24. package/dist-embed/chunks/{he-IL-6SHJWFNN-D4A-WPuC.js → he-IL-6SHJWFNN-Dm7uysTx.js} +1 -1
  25. package/dist-embed/chunks/{hi-IN-IWLTKZ5I-DO3QdFAK.js → hi-IN-IWLTKZ5I-CeY2Albs.js} +1 -1
  26. package/dist-embed/chunks/{hu-HU-A5ZG7DT2-BOPacqST.js → hu-HU-A5ZG7DT2-DCDid3ls.js} +1 -1
  27. package/dist-embed/chunks/{id-ID-SAP4L64H-BDQ5wEDe.js → id-ID-SAP4L64H-Cu9nTzN4.js} +1 -1
  28. package/dist-embed/chunks/{index-DjTtL0TF.js → index-BlWhVENa.js} +1 -1
  29. package/dist-embed/chunks/{index-B9M5uQX3.js → index-DNxREKJB.js} +1 -1
  30. package/dist-embed/chunks/{it-IT-JPQ66NNP-bzbg9EMd.js → it-IT-JPQ66NNP-BFikXjfD.js} +1 -1
  31. package/dist-embed/chunks/{ja-JP-DBVTYXUO-Szvm49iH.js → ja-JP-DBVTYXUO-BmKD-yse.js} +1 -1
  32. package/dist-embed/chunks/{kaa-6HZHGXH3-CmjctWJC.js → kaa-6HZHGXH3-Db041Jip.js} +1 -1
  33. package/dist-embed/chunks/{kab-KAB-ZGHBKWFO-2neAyT0K.js → kab-KAB-ZGHBKWFO-BCvIk325.js} +1 -1
  34. package/dist-embed/chunks/{kk-KZ-P5N5QNE5-DFBQnc6O.js → kk-KZ-P5N5QNE5-BuOKPJvy.js} +1 -1
  35. package/dist-embed/chunks/{km-KH-HSX4SM5Z-BBsrLBf3.js → km-KH-HSX4SM5Z-BMHxKgUF.js} +1 -1
  36. package/dist-embed/chunks/{ko-KR-MTYHY66A-BhpsFd8K.js → ko-KR-MTYHY66A-CrGHLzSs.js} +1 -1
  37. package/dist-embed/chunks/{ku-TR-6OUDTVRD-BkmYi70s.js → ku-TR-6OUDTVRD-OWTQ-E4j.js} +1 -1
  38. package/dist-embed/chunks/{lt-LT-XHIRWOB4-BQzLQ3IM.js → lt-LT-XHIRWOB4-cWU3pPV4.js} +1 -1
  39. package/dist-embed/chunks/{lv-LV-5QDEKY6T-C2uG3w1b.js → lv-LV-5QDEKY6T-BxDTvB6y.js} +1 -1
  40. package/dist-embed/chunks/{mr-IN-CRQNXWMA-c0ua0Wep.js → mr-IN-CRQNXWMA-CqyfFYLc.js} +1 -1
  41. package/dist-embed/chunks/{my-MM-5M5IBNSE-CHwcQNVK.js → my-MM-5M5IBNSE-Cx7zaUxn.js} +1 -1
  42. package/dist-embed/chunks/{nb-NO-T6EIAALU-ExHLJdKL.js → nb-NO-T6EIAALU-B2mtxO3c.js} +1 -1
  43. package/dist-embed/chunks/{nl-NL-IS3SIHDZ-BZVh9P58.js → nl-NL-IS3SIHDZ-CXUwM9QK.js} +1 -1
  44. package/dist-embed/chunks/{nn-NO-6E72VCQL-B0Lsdzal.js → nn-NO-6E72VCQL-CHyB0wsS.js} +1 -1
  45. package/dist-embed/chunks/{oc-FR-POXYY2M6-6h1IfczT.js → oc-FR-POXYY2M6-PWsq-UBi.js} +1 -1
  46. package/dist-embed/chunks/{pa-IN-N4M65BXN-BatFUiwy.js → pa-IN-N4M65BXN-CY1sLit6.js} +1 -1
  47. package/dist-embed/chunks/{percentages-BXMCSKIN-CUieN6MU.js → percentages-BXMCSKIN-B8HDDw-F.js} +3 -3
  48. package/dist-embed/chunks/{pl-PL-T2D74RX3-DX6A7KgW.js → pl-PL-T2D74RX3-BsM9Abo3.js} +1 -1
  49. package/dist-embed/chunks/{pt-BR-5N22H2LF-N7HLFrzO.js → pt-BR-5N22H2LF-BNWoCeUd.js} +1 -1
  50. package/dist-embed/chunks/{pt-PT-UZXXM6DQ-DUZk-o8n.js → pt-PT-UZXXM6DQ-DBoCufKc.js} +1 -1
  51. package/dist-embed/chunks/{ro-RO-JPDTUUEW-eOhj0vob.js → ro-RO-JPDTUUEW-K_k74TtF.js} +1 -1
  52. package/dist-embed/chunks/{ru-RU-B4JR7IUQ-adV0gXfd.js → ru-RU-B4JR7IUQ-CGEGiyDu.js} +1 -1
  53. package/dist-embed/chunks/{si-LK-N5RQ5JYF-Bw2cyfz1.js → si-LK-N5RQ5JYF-BViNwnfQ.js} +1 -1
  54. package/dist-embed/chunks/{sk-SK-C5VTKIMK-CKE2DmyW.js → sk-SK-C5VTKIMK-DU3L7Dsq.js} +1 -1
  55. package/dist-embed/chunks/{sl-SI-NN7IZMDC-CFc8Y3VE.js → sl-SI-NN7IZMDC-DKaPsJRy.js} +1 -1
  56. package/dist-embed/chunks/{subset-shared.chunk-Dma4VCy8.js → subset-shared.chunk-BMSr0xzY.js} +1 -1
  57. package/dist-embed/chunks/{subset-worker.chunk-Bz00tIvB.js → subset-worker.chunk-CtzfGuvc.js} +1 -1
  58. package/dist-embed/chunks/{sv-SE-XGPEYMSR-BbadENuL.js → sv-SE-XGPEYMSR-CIE6pYzb.js} +1 -1
  59. package/dist-embed/chunks/{ta-IN-2NMHFXQM-DIcMv2m_.js → ta-IN-2NMHFXQM-n0YSquYB.js} +1 -1
  60. package/dist-embed/chunks/{th-TH-HPSO5L25-Dg_SsP8U.js → th-TH-HPSO5L25-Dqs6nDFF.js} +1 -1
  61. package/dist-embed/chunks/{tr-TR-DEFEU3FU-CT-y06oR.js → tr-TR-DEFEU3FU-DV00_VI-.js} +1 -1
  62. package/dist-embed/chunks/{uk-UA-QMV73CPH-BZ5Ohlyz.js → uk-UA-QMV73CPH-Brc-9Imh.js} +1 -1
  63. package/dist-embed/chunks/{vi-VN-M7AON7JQ-BJsFLDTl.js → vi-VN-M7AON7JQ-Dpi-23XP.js} +1 -1
  64. package/dist-embed/chunks/{zh-CN-LNUGB5OW-BEBj3lBa.js → zh-CN-LNUGB5OW-OieOX5TM.js} +1 -1
  65. package/dist-embed/chunks/{zh-HK-E62DVLB3-DaP5iF63.js → zh-HK-E62DVLB3-DXp_54pc.js} +1 -1
  66. package/dist-embed/chunks/{zh-TW-RAJ6MFWO-Du-01YeM.js → zh-TW-RAJ6MFWO-DGPCsgPG.js} +1 -1
  67. package/dist-embed/datalayer-app-main.js +56 -56
  68. package/lib/apps/apps/appspec.js +1 -0
  69. package/lib/chat/base/ChatBase.js +2 -2
  70. package/lib/chat/base/conversationItems.d.ts +7 -0
  71. package/lib/chat/base/conversationItems.js +30 -0
  72. package/lib/portable/hosts/contracts.d.ts +1 -1
  73. package/lib/portable/hosts/contracts.js +1 -1
  74. package/lib/portable/hosts/contracts.json +59 -1
  75. package/lib/portable/hosts/forbiddenGraph.js +1 -0
  76. package/lib/portable/hosts/index.d.ts +1 -1
  77. package/lib/portable/hosts/index.js +1 -1
  78. package/lib/portable/hosts/sessionUid.d.ts +3 -1
  79. package/lib/portable/hosts/sessionUid.js +3 -1
  80. package/lib/portable/semantic-events.json +117 -2
  81. package/lib/portable/semantic.d.ts +13 -1
  82. package/lib/portable/semantic.js +1 -12
  83. package/lib/portable/semanticOfAgUi.js +15 -3
  84. package/lib/specs/appspecSchema.js +2 -1
  85. package/lib/types/agentspecs.d.ts +1 -1
  86. package/lib/types/tools.d.ts +6 -0
  87. package/package.json +1 -1
  88. package/dist-embed/assets/loro_wasm_bg-BjoaHtGs.wasm +0 -0
@@ -36,6 +36,7 @@ export const APP_THEME_VARIANTS = [
36
36
  'ivory',
37
37
  'sun',
38
38
  'loop',
39
+ 'vscode',
39
40
  ];
40
41
  /** The colour modes a theme is worn in. */
41
42
  export const APP_THEME_MODES = ['light', 'dark', 'auto'];
@@ -39,7 +39,7 @@ import { AI_MODEL_CATALOGUE, DEFAULT_MODEL, isChatModel, } from '../../specs/mod
39
39
  import { internalQueryClient, isToolCallMessage, getMessageText, createProtocolAdapter, getApiBaseFromConfig, sanitizeAssistantContent, } from '../../utils/index.js';
40
40
  import { BROWSER_LIMITS, emptyConversation, HARNESS_TOOL_CALL, reduceConversation, withError, withoutItems, withPrompt, withTurnEnded, } from '../../portable/conversation.js';
41
41
  import { conversationEventsOf, snapshotOfMessages, } from '../../portable/adapterEvents.js';
42
- import { conversationDrawer, emptyChatLayer, rowIdOf, toolCallOf, } from './conversationItems.js';
42
+ import { conversationDrawer, emptyChatLayer, rowIdOf, toolCallOf, withToolSummaries, } from './conversationItems.js';
43
43
  import { useConfig, useSkills, useSkillActions, useContextSnapshot, useSandbox, } from '../../hooks/index.js';
44
44
  import { useAgentRuntimeWebSocket } from '../../hooks/useAgentRuntimes.js';
45
45
  import { agentRuntimeStore, useAgentRuntimeStore, useAgentRuntimeWsState, } from '../../stores/agentRuntimeStore.js';
@@ -1163,7 +1163,7 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
1163
1163
  const chatTextRef = useRef(chatText);
1164
1164
  chatTextRef.current = chatText;
1165
1165
  const drawConversation = useMemo(() => conversationDrawer(text => chatTextRef.current.error(text)), []);
1166
- const displayItems = useMemo(() => drawConversation(conversation, layer), [drawConversation, conversation, layer]);
1166
+ const displayItems = useMemo(() => withToolSummaries(drawConversation(conversation, layer), frontendTools), [drawConversation, conversation, layer, frontendTools]);
1167
1167
  const displayItemsRef = useRef(displayItems);
1168
1168
  displayItemsRef.current = displayItems;
1169
1169
  /*
@@ -16,6 +16,7 @@ import type { Display } from '@datalayer/reactor/ui';
16
16
  import type { ConversationItem, ConversationState } from '../../portable/conversation';
17
17
  import type { DisplayItem, DisplayToolCallStatus, ToolCallMessage } from '../../types/chat';
18
18
  import type { ChatMessage, MessageSpeaker } from '../../types/messages';
19
+ import type { FrontendToolDefinition } from '../../types/tools';
19
20
  type ToolItem = Extract<ConversationItem, {
20
21
  kind: 'tool';
21
22
  }>;
@@ -46,6 +47,12 @@ export declare const rowIdOf: (item: DisplayItem) => string;
46
47
  export declare function cardStatusOf(item: ToolItem): DisplayToolCallStatus;
47
48
  /** A tool call as its card draws it. */
48
49
  export declare function toolCallOf(item: ToolItem): ToolCallMessage;
50
+ /**
51
+ * The cards of the page's own tools, with what each says of its call
52
+ * (`summaryOf`) once it returned. A card nothing changes is the one drawn,
53
+ * so React leaves it as it was.
54
+ */
55
+ export declare function withToolSummaries(items: DisplayItem[], tools?: readonly Pick<FrontendToolDefinition, 'name' | 'summaryOf'>[]): DisplayItem[];
49
56
  /**
50
57
  * A drawer of a conversation: each row drawn once, and drawn again only when
51
58
  * it, or what the web holds beside it, changed — so a message streaming
@@ -2,6 +2,7 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
+ import { isToolCallMessage } from '../../utils/index.js';
5
6
  export const emptyChatLayer = () => ({
6
7
  extras: new Map(),
7
8
  displays: new Map(),
@@ -52,6 +53,35 @@ export function toolCallOf(item) {
52
53
  ...(item.exitCode !== null ? { exitCode: item.exitCode } : {}),
53
54
  };
54
55
  }
56
+ /**
57
+ * The cards of the page's own tools, with what each says of its call
58
+ * (`summaryOf`) once it returned. A card nothing changes is the one drawn,
59
+ * so React leaves it as it was.
60
+ */
61
+ export function withToolSummaries(items, tools = []) {
62
+ const summarizing = tools.filter(tool => tool.summaryOf);
63
+ if (summarizing.length === 0) {
64
+ return items;
65
+ }
66
+ let changed = false;
67
+ const said = items.map(item => {
68
+ if (!isToolCallMessage(item)) {
69
+ return item;
70
+ }
71
+ const call = item;
72
+ const tool = summarizing.find(entry => entry.name === call.toolName);
73
+ if (!tool?.summaryOf || call.result === undefined) {
74
+ return item;
75
+ }
76
+ const summary = tool.summaryOf(call.args, call.result)?.trim();
77
+ if (!summary || summary === call.summary) {
78
+ return item;
79
+ }
80
+ changed = true;
81
+ return { ...call, summary };
82
+ });
83
+ return changed ? said : items;
84
+ }
55
85
  /**
56
86
  * A drawer of a conversation: each row drawn once, and drawn again only when
57
87
  * it, or what the web holds beside it, changed — so a message streaming
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
2
  /**
3
- * The generic Datalayer host contracts, as ai-agents answers them, for every chat host (Slack, Discord).
3
+ * The generic Datalayer host contracts, as ai-agents answers them, for every chat host (Slack, Discord, WhatsApp).
4
4
  * These shapes are shared with ai-agents through `contracts.json`, beside
5
5
  * this file and byte-identical to ai-agents' `tests/fixtures/hosts/`, which
6
6
  * both sides test. A delivery's `target` stays opaque here: each host
@@ -5,7 +5,7 @@
5
5
  import { createHmac, timingSafeEqual } from 'node:crypto';
6
6
  import { z } from 'zod';
7
7
  /**
8
- * The generic Datalayer host contracts, as ai-agents answers them, for every chat host (Slack, Discord).
8
+ * The generic Datalayer host contracts, as ai-agents answers them, for every chat host (Slack, Discord, WhatsApp).
9
9
  * These shapes are shared with ai-agents through `contracts.json`, beside
10
10
  * this file and byte-identical to ai-agents' `tests/fixtures/hosts/`, which
11
11
  * both sides test. A delivery's `target` stays opaque here: each host
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 1,
3
- "about": "The generic host contracts of the chat hosts, as ai-agents and the runtime's session API answer them. Shared verbatim by ai-agents (tests/fixtures/hosts) and agent-runtimes (src/portable/hosts, read by the Slack and Discord adapters from lib/portable/hosts): each side's tests check its half against this file.",
3
+ "about": "The generic host contracts of the chat hosts, as ai-agents and the runtime's session API answer them. Shared verbatim by ai-agents (tests/fixtures/hosts) and agent-runtimes (src/portable/hosts, read by the Slack, Discord and WhatsApp adapters from lib/portable/hosts): each side's tests check its half against this file.",
4
4
  "headers": {
5
5
  "host_key": "X-Datalayer-Host-Key",
6
6
  "signature": "X-Datalayer-Signature",
@@ -493,5 +493,63 @@
493
493
  "threadId": "456789012345678901",
494
494
  "userId": "567890123456789012"
495
495
  }
496
+ },
497
+ "whatsapp": {
498
+ "about": "WhatsApp's forms of the generic contracts: the installation is <waba_id>:<phone_number_id>, one per connected business number; a person is their business-scoped user id (BSUID: a country code, a period and up to 128 characters; never the phone number), which the external id pattern admits as it is, no hash in between; a 1:1 chat is a direct place named by the BSUID, a group a channel named by its group id; the session uid is hostSessionUid(\"whatsapp\", [waba_id, phone_number_id, user_id]) for a chat and [waba_id, phone_number_id, group_id] for a group; a turn's provenance names the message by its wamid.",
499
+ "waba_id": "102290129340398",
500
+ "phone_number_id": "106540352242922",
501
+ "bind_request": {
502
+ "code": "dlhc_…",
503
+ "external_id": "102290129340398:106540352242922",
504
+ "name": "Acme"
505
+ },
506
+ "external_user_id": "US.13491208655302741918",
507
+ "external_user_name": "Alice",
508
+ "external_user_id_longest": "US.11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
509
+ "direct": {
510
+ "place": {
511
+ "kind": "direct",
512
+ "id": "US.13491208655302741918"
513
+ }
514
+ },
515
+ "group": {
516
+ "id": "120363025246125486",
517
+ "place": {
518
+ "kind": "channel",
519
+ "id": "120363025246125486"
520
+ }
521
+ },
522
+ "session": {
523
+ "parts": [
524
+ "102290129340398",
525
+ "106540352242922",
526
+ "US.13491208655302741918"
527
+ ],
528
+ "uid": "whatsapp-870e7e8165519947ec9c7df03d378b342cee380e"
529
+ },
530
+ "group_session": {
531
+ "parts": [
532
+ "102290129340398",
533
+ "106540352242922",
534
+ "120363025246125486"
535
+ ],
536
+ "uid": "whatsapp-49aeadff03b2d02e6359121f9a55bae904640b35"
537
+ },
538
+ "provenance": {
539
+ "host_kind": "whatsapp",
540
+ "place": {
541
+ "kind": "direct",
542
+ "id": "US.13491208655302741918"
543
+ },
544
+ "message_id": "wamid.HBgLMTU1NTEyMzQ1NjcVAgASGBQzQUY3RjU0QjRFRjc2MzBCNkJCNAA="
545
+ },
546
+ "target": {
547
+ "phoneNumberId": "106540352242922",
548
+ "userId": "US.13491208655302741918"
549
+ },
550
+ "group_target": {
551
+ "phoneNumberId": "106540352242922",
552
+ "groupId": "120363025246125486"
553
+ }
496
554
  }
497
555
  }
@@ -26,6 +26,7 @@ export const FORBIDDEN = [
26
26
  /^@datalayer\/reactor$/,
27
27
  /^@datalayer\/slack$/,
28
28
  /^@datalayer\/discord$/,
29
+ /^@datalayer\/whatsapp$/,
29
30
  ];
30
31
  export const isForbidden = (name) => FORBIDDEN.some(pattern => pattern.test(name));
31
32
  /** The package name an import specifier names: `@scope/name/deep` → `@scope/name`. */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * What a chat host service (Slack, Discord) shares, on Node: the ai-agents
2
+ * What a chat host service (Slack, Discord, WhatsApp) shares, on Node: the ai-agents
3
3
  * host contracts and client, the agent client over the session API, the
4
4
  * semantic events a turn is reduced to, approval renderers, deliveries, the
5
5
  * turn's state, application resolution, the durable queue, quotas, secrets
@@ -3,7 +3,7 @@
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
5
  /**
6
- * What a chat host service (Slack, Discord) shares, on Node: the ai-agents
6
+ * What a chat host service (Slack, Discord, WhatsApp) shares, on Node: the ai-agents
7
7
  * host contracts and client, the agent client over the session API, the
8
8
  * semantic events a turn is reduced to, approval renderers, deliveries, the
9
9
  * turn's state, application resolution, the durable queue, quotas, secrets
@@ -2,6 +2,8 @@
2
2
  * `<kind>-<40 hex>`: the SHA-256 of the thread's parts joined by `:`, and
3
3
  * `:<generation>` once the thread started over or switched application. The
4
4
  * same parts give the same uid (Slack: app, enterprise or `-`, team,
5
- * channel, thread ts; Discord: application, guild, channel, thread).
5
+ * channel, thread ts; Discord: application, guild, channel, thread;
6
+ * WhatsApp: WABA, phone number id, and the person's BSUID for a chat or the
7
+ * group's id for a group).
6
8
  */
7
9
  export declare function hostSessionUid(kind: string, parts: readonly string[], generation?: number): string;
@@ -13,7 +13,9 @@ import { createHash } from 'node:crypto';
13
13
  * `<kind>-<40 hex>`: the SHA-256 of the thread's parts joined by `:`, and
14
14
  * `:<generation>` once the thread started over or switched application. The
15
15
  * same parts give the same uid (Slack: app, enterprise or `-`, team,
16
- * channel, thread ts; Discord: application, guild, channel, thread).
16
+ * channel, thread ts; Discord: application, guild, channel, thread;
17
+ * WhatsApp: WABA, phone number id, and the person's BSUID for a chat or the
18
+ * group's id for a group).
17
19
  */
18
20
  export function hostSessionUid(kind, parts, generation) {
19
21
  if (!/^[a-z][a-z0-9-]{1,31}$/.test(kind)) {
@@ -12,7 +12,8 @@
12
12
  "ask",
13
13
  "completion",
14
14
  "stop",
15
- "error"
15
+ "error",
16
+ "sign-in"
16
17
  ],
17
18
  "producers": {
18
19
  "text": "AG-UI TEXT_MESSAGE_CONTENT / TEXT_MESSAGE_CHUNK delta",
@@ -25,7 +26,8 @@
25
26
  "ask": "AG-UI RUN_FINISHED with result.asking (agent_runtimes/loop/apps/sessions.py question_of); the run stream carries no loop.ask event",
26
27
  "completion": "AG-UI RUN_FINISHED without asking",
27
28
  "stop": "the host itself, after its own Stop: a stopped session's stream ends without RUN_FINISHED or RUN_ERROR (agent_runtimes/loop/apps/sessions.py stop, _close); no AG-UI event carries it",
28
- "error": "AG-UI RUN_ERROR message"
29
+ "error": "AG-UI RUN_ERROR message",
30
+ "sign-in": "AG-UI TOOL_CALL_RESULT of pap_sign_in (or pact_sign_in, with or without a gateway prefix) whose result is a sign-in waiting for the person with an https page (src/portable/signIn.ts readPapSignIn), after the tool's complete row; a page that is not https gives the row only"
29
31
  },
30
32
  "cases": [
31
33
  {
@@ -447,6 +449,119 @@
447
449
  }
448
450
  ],
449
451
  "semantic": []
452
+ },
453
+ {
454
+ "agui": [
455
+ {
456
+ "type": "TOOL_CALL_START",
457
+ "toolCallId": "c9",
458
+ "toolCallName": "pap_sign_in"
459
+ },
460
+ {
461
+ "type": "TOOL_CALL_RESULT",
462
+ "toolCallId": "c9",
463
+ "messageId": "r9",
464
+ "content": "{\"status\": \"waiting_for_the_person\", \"mode\": \"direct\", \"open\": \"https://iam.example.com/pap/start/t1\", \"company\": \"manzanita.example\", \"organization_name\": \"Manzanita\", \"organization_domain\": \"manzanita.example\"}"
465
+ }
466
+ ],
467
+ "semantic": [
468
+ {
469
+ "kind": "tool",
470
+ "id": "c9",
471
+ "name": "pap_sign_in",
472
+ "status": "running"
473
+ },
474
+ {
475
+ "kind": "tool",
476
+ "id": "c9",
477
+ "name": "pap_sign_in",
478
+ "status": "complete"
479
+ },
480
+ {
481
+ "kind": "sign-in",
482
+ "toolCallId": "c9",
483
+ "toolName": "pap_sign_in",
484
+ "signIn": {
485
+ "mode": "direct",
486
+ "open": "https://iam.example.com/pap/start/t1",
487
+ "host": "iam.example.com",
488
+ "company": "manzanita.example",
489
+ "organization": {
490
+ "name": "Manzanita",
491
+ "domain": "manzanita.example"
492
+ }
493
+ }
494
+ }
495
+ ]
496
+ },
497
+ {
498
+ "agui": [
499
+ {
500
+ "type": "TOOL_CALL_START",
501
+ "toolCallId": "c10",
502
+ "toolCallName": "gateway_pap_sign_in"
503
+ },
504
+ {
505
+ "type": "TOOL_CALL_RESULT",
506
+ "toolCallId": "c10",
507
+ "messageId": "r10",
508
+ "content": "{\"structuredContent\": {\"status\": \"waiting_for_the_person\", \"mode\": \"device\", \"open\": \"https://manzanita.example/device\", \"code\": \"WDJB-MJHT\"}}"
509
+ }
510
+ ],
511
+ "semantic": [
512
+ {
513
+ "kind": "tool",
514
+ "id": "c10",
515
+ "name": "gateway_pap_sign_in",
516
+ "status": "running"
517
+ },
518
+ {
519
+ "kind": "tool",
520
+ "id": "c10",
521
+ "name": "gateway_pap_sign_in",
522
+ "status": "complete"
523
+ },
524
+ {
525
+ "kind": "sign-in",
526
+ "toolCallId": "c10",
527
+ "toolName": "gateway_pap_sign_in",
528
+ "signIn": {
529
+ "mode": "device",
530
+ "open": "https://manzanita.example/device",
531
+ "host": "manzanita.example",
532
+ "code": "WDJB-MJHT"
533
+ }
534
+ }
535
+ ]
536
+ },
537
+ {
538
+ "agui": [
539
+ {
540
+ "type": "TOOL_CALL_START",
541
+ "toolCallId": "c11",
542
+ "toolCallName": "pap_sign_in"
543
+ },
544
+ {
545
+ "type": "TOOL_CALL_RESULT",
546
+ "toolCallId": "c11",
547
+ "messageId": "r11",
548
+ "content": "{\"status\": \"waiting_for_the_person\", \"mode\": \"direct\", \"open\": \"http://manzanita.example/signin\"}"
549
+ }
550
+ ],
551
+ "semantic": [
552
+ {
553
+ "kind": "tool",
554
+ "id": "c11",
555
+ "name": "pap_sign_in",
556
+ "status": "running"
557
+ },
558
+ {
559
+ "kind": "tool",
560
+ "id": "c11",
561
+ "name": "pap_sign_in",
562
+ "status": "complete"
563
+ }
564
+ ]
450
565
  }
451
566
  ],
452
567
  "answers": [
@@ -10,8 +10,9 @@
10
10
  *
11
11
  * @module portable/semantic
12
12
  */
13
+ import type { PapSignInWaiting } from './signIn';
13
14
  export declare const SEMANTIC_EVENTS_VERSION = 1;
14
- export declare const SEMANTIC_KINDS: readonly ["text", "progress", "tool", "approval", "table", "file", "citation", "ask", "completion", "stop", "error"];
15
+ export declare const SEMANTIC_KINDS: readonly ["text", "progress", "tool", "approval", "table", "file", "citation", "ask", "completion", "stop", "error", "sign-in"];
15
16
  export type SemanticKind = (typeof SEMANTIC_KINDS)[number];
16
17
  export type AskQuestion = Readonly<{
17
18
  /** `text` and `choice` are answered in the host; `file` and `form` hand off to Datalayer. */
@@ -58,6 +59,17 @@ export type SemanticEvent = Readonly<{
58
59
  }> | Readonly<{
59
60
  kind: 'error';
60
61
  message: string;
62
+ }> | Readonly<{
63
+ /**
64
+ * A company's sign-in waiting for the person (`pap_sign_in`, or a
65
+ * business's `pact_sign_in`): its page (https only), mode and code,
66
+ * as `signIn.ts` reads them. A host shows it as a button or the code
67
+ * with the page; the turn goes on once the person has signed in.
68
+ */
69
+ kind: 'sign-in';
70
+ toolCallId: string;
71
+ toolName: string;
72
+ signIn: PapSignInWaiting;
61
73
  }>;
62
74
  /** Whether an event ends the turn's presentation. */
63
75
  export declare function isTerminal(event: SemanticEvent): boolean;
@@ -2,18 +2,6 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
- /**
6
- * The semantic agent events a chat host renders (Slack, Discord), version 1:
7
- * a session's AG-UI stream reduced to what a host shows (`semanticOfAgUi.ts`),
8
- * never a notebook widget or a web component. The contract and its fixture
9
- * cases are `semantic-events.json`, beside this file, tested here
10
- * (`__tests__/semantic.unit.test.ts`) and by the runtime that emits the
11
- * events (`agent_runtimes/tests/test_semantic_events_contract.py`).
12
- *
13
- * Moved from the Slack service (DC-005, S-004, S-205).
14
- *
15
- * @module portable/semantic
16
- */
17
5
  export const SEMANTIC_EVENTS_VERSION = 1;
18
6
  export const SEMANTIC_KINDS = [
19
7
  'text',
@@ -27,6 +15,7 @@ export const SEMANTIC_KINDS = [
27
15
  'completion',
28
16
  'stop',
29
17
  'error',
18
+ 'sign-in',
30
19
  ];
31
20
  /** Whether an event ends the turn's presentation. */
32
21
  export function isTerminal(event) {
@@ -2,6 +2,7 @@
2
2
  * Copyright (c) 2025-2026 Datalayer, Inc.
3
3
  * Distributed under the terms of the Modified BSD License.
4
4
  */
5
+ import { isPapSignInTool, readPapSignIn } from './signIn.js';
5
6
  /** A string field of an event, or the fallback: never an object stringified. */
6
7
  const stringOf = (value, fallback = '') => typeof value === 'string' ? value : fallback;
7
8
  const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
@@ -84,9 +85,20 @@ export function semanticOf(event, tools) {
84
85
  return [{ kind: 'approval', toolCallId: id, toolName: name }];
85
86
  }
86
87
  const failed = result?.success === false || result?.isError === true;
87
- return [
88
- { kind: 'tool', id, name, status: failed ? 'failed' : 'complete' },
89
- ];
88
+ const done = {
89
+ kind: 'tool',
90
+ id,
91
+ name,
92
+ status: failed ? 'failed' : 'complete',
93
+ };
94
+ // A company's sign-in waiting for the person: the host shows its page.
95
+ // A page that is not https is never shown: the tool's row says it.
96
+ const signIn = isPapSignInTool(name)
97
+ ? readPapSignIn(event.content)
98
+ : null;
99
+ return signIn && !('refused' in signIn)
100
+ ? [done, { kind: 'sign-in', toolCallId: id, toolName: name, signIn }]
101
+ : [done];
90
102
  }
91
103
  case 'STEP_STARTED':
92
104
  return typeof event.stepName === 'string' && event.stepName
@@ -1961,7 +1961,7 @@ export const APPSPEC_SCHEMA = {
1961
1961
  properties: {
1962
1962
  variant: {
1963
1963
  $ref: '#/$defs/ThemeVariant',
1964
- description: 'The theme: `datalayer`, `spatial`, `lovely`, `matrix`, `earth`, `sand`, `ivory`, `sun` or `loop`',
1964
+ description: 'The theme: `datalayer`, `spatial`, `lovely`, `matrix`, `earth`, `sand`, `ivory`, `sun`, `loop` or `vscode`',
1965
1965
  },
1966
1966
  mode: {
1967
1967
  anyOf: [
@@ -2505,6 +2505,7 @@ export const APPSPEC_SCHEMA = {
2505
2505
  'ivory',
2506
2506
  'sun',
2507
2507
  'loop',
2508
+ 'vscode',
2508
2509
  ],
2509
2510
  title: 'ThemeVariant',
2510
2511
  type: 'string',
@@ -717,7 +717,7 @@ export type AppLayout = 'chat' | 'page' | 'split';
717
717
  /** The one colour of an application; everything else is neutral. */
718
718
  export type AppAccent = 'green' | 'rose' | 'sky' | 'lime' | 'sun' | 'violet';
719
719
  /** A theme of Appearance's, which an application may run in (T-30). */
720
- export type AppThemeVariant = 'datalayer' | 'spatial' | 'lovely' | 'matrix' | 'earth' | 'sand' | 'ivory' | 'sun' | 'loop';
720
+ export type AppThemeVariant = 'datalayer' | 'spatial' | 'lovely' | 'matrix' | 'earth' | 'sand' | 'ivory' | 'sun' | 'loop' | 'vscode';
721
721
  /** The colour mode it is worn in: `auto` follows the device. */
722
722
  export type AppThemeMode = 'light' | 'dark' | 'auto';
723
723
  /**
@@ -149,6 +149,12 @@ export interface FrontendToolDefinition<TArgs = Record<string, unknown>, TResult
149
149
  * Required when location is 'frontend' (default)
150
150
  */
151
151
  handler?: (args: TArgs) => Promise<TResult>;
152
+ /**
153
+ * What its card says in place of its arguments' summary, from what it
154
+ * returned (`on analysis.ipynb`, the file a host's editor tool ran on);
155
+ * `undefined` keeps the arguments' summary. Asked only once it returned.
156
+ */
157
+ summaryOf?: (args: TArgs, result: TResult) => string | undefined;
152
158
  /**
153
159
  * Optional render function for custom UI during execution
154
160
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.5.83",
3
+ "version": "1.5.85",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",