@datalayer/agent-runtimes 1.5.15 → 1.5.16

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.
@@ -32,6 +32,8 @@ import { useCoreStore } from '@datalayer/core';
32
32
  import { DEFAULT_SERVICE_URLS } from '@datalayer/core/lib/api/constants';
33
33
  import { useChatStore } from '../../stores/chatStore';
34
34
  import { useConversationStore } from '../../stores/conversationStore';
35
+ import { askedOf } from '../../apps/apps/records';
36
+ import { extractChatMessagesFromFullContext } from './fullContextMessages';
35
37
  import { generateMessageId, createUserMessage, createAssistantMessage, } from '../../types/messages';
36
38
  import { AI_MODEL_CATALOGUE, DEFAULT_MODEL, isChatModel, } from '../../specs/models';
37
39
  import { internalQueryClient, isToolCallMessage, convertHistoryToDisplayItems, createProtocolAdapter, getApiBaseFromConfig, sanitizeAssistantContent, } from '../../utils';
@@ -315,99 +317,6 @@ function formatToolResultFallback(result) {
315
317
  return 'Tool completed successfully.';
316
318
  }
317
319
  }
318
- function extractChatMessagesFromFullContext(fullContext) {
319
- if (!fullContext) {
320
- return [];
321
- }
322
- const rawMessages = Array.isArray(fullContext.messages)
323
- ? fullContext.messages
324
- : [];
325
- return rawMessages
326
- .map((msg, index) => {
327
- const role = String(msg.role || '').toLowerCase();
328
- const timestampValue = typeof msg.timestamp === 'string' && msg.timestamp.length > 0
329
- ? msg.timestamp
330
- : new Date().toISOString();
331
- const createdAtRaw = new Date(timestampValue);
332
- const createdAt = Number.isNaN(createdAtRaw.getTime())
333
- ? new Date()
334
- : createdAtRaw;
335
- const rawContent = typeof msg.content === 'string'
336
- ? msg.content
337
- : JSON.stringify(msg.content ?? '');
338
- const isToolCall = Boolean(msg.isToolCall);
339
- const isToolResult = Boolean(msg.isToolResult);
340
- const toolCallId = typeof msg.toolCallId === 'string' && msg.toolCallId.length > 0
341
- ? msg.toolCallId
342
- : undefined;
343
- const toolName = typeof msg.toolName === 'string' && msg.toolName.length > 0
344
- ? msg.toolName
345
- : undefined;
346
- // Tool-call turns arrive as assistant messages whose `content` is the
347
- // JSON-encoded tool arguments. Reconstruct a structured `toolCalls`
348
- // entry so it renders as a tool card (matching the live stream) instead
349
- // of leaking raw JSON into the transcript.
350
- if (isToolCall) {
351
- let args = {};
352
- if (rawContent) {
353
- try {
354
- const parsed = JSON.parse(rawContent);
355
- if (parsed &&
356
- typeof parsed === 'object' &&
357
- !Array.isArray(parsed)) {
358
- args = parsed;
359
- }
360
- }
361
- catch {
362
- args = {};
363
- }
364
- }
365
- const resolvedToolCallId = toolCallId || `history-tc-${index}`;
366
- return {
367
- id: `history-toolcall-${index}-${timestampValue}`,
368
- role: 'assistant',
369
- content: '',
370
- createdAt,
371
- toolCalls: [
372
- {
373
- type: 'tool-call',
374
- toolCallId: resolvedToolCallId,
375
- toolName: toolName || 'tool',
376
- args,
377
- status: 'completed',
378
- },
379
- ],
380
- };
381
- }
382
- // Tool results arrive as `role: 'tool'` messages; keep them (with the
383
- // matching toolCallId) so `convertHistoryToDisplayItems` can merge the
384
- // result into its tool card.
385
- if (isToolResult || role === 'tool') {
386
- return {
387
- id: `history-toolresult-${index}-${timestampValue}`,
388
- role: 'tool',
389
- content: rawContent,
390
- createdAt,
391
- metadata: {
392
- toolCallId,
393
- toolName,
394
- },
395
- };
396
- }
397
- // Only hydrate conversational turns in the visible history.
398
- // System messages may contain internal prompts and metadata.
399
- if (role !== 'user' && role !== 'assistant') {
400
- return null;
401
- }
402
- return {
403
- id: `history-${role}-${index}-${timestampValue}`,
404
- role,
405
- content: rawContent,
406
- createdAt,
407
- };
408
- })
409
- .filter((m) => m !== null);
410
- }
411
320
  function parseEnabledMcpToolsByServer(mcpStatusData) {
412
321
  if (!mcpStatusData || typeof mcpStatusData !== 'object') {
413
322
  return null;
@@ -3867,7 +3776,9 @@ showToolApprovalBanner = true, pendingApprovals: pendingApprovalsProp, onApprove
3867
3776
  typingSuggestions: resolvedSuggestions?.map(item => item.message || item.title), autoFocus: autoFocus, focusTrigger: focusTrigger, padding: padding, onSend: () => handleSend(), onStop: handleStop, disableInputPrompt: disableInputPrompt || disabled || !!overlay || launching, promptVariant: promptVariant, mentionableAgents: mentionableAgents, promptCommands: promptCommands,
3868
3777
  // The runtime's memory of what was sent to this agent, for the arrow
3869
3778
  // keys — part of the same initial state as the models and tools.
3870
- promptHistory: configQuery.data?.promptHistory, showTokenUsage: showTokenUsage, showContextRing: showContextRing, agentUsage: agentUsage, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, isA2AProtocol: isA2AProtocol, showAgentsMenu: showAgentsMenu, agents: footerAgents, selectedAgentId: selectedAgentId ?? footerAgents[0]?.id, onSelectAgent: onSelectAgent, hasConfigData: !!configQuery.data, hasSkillsData: !!skillsQuery.data,
3779
+ // Without what the runtime told the application before a personal
3780
+ // agent's request (`askedOf`): the person never wrote it.
3781
+ promptHistory: configQuery.data?.promptHistory?.map(askedOf), showTokenUsage: showTokenUsage, showContextRing: showContextRing, agentUsage: agentUsage, showModelSelector: showModelSelector, showToolsMenu: showToolsMenu, showSkillsMenu: showSkillsMenu, codemodeEnabled: codemodeEnabled, onToggleCodemode: onToggleCodemode, isA2AProtocol: isA2AProtocol, showAgentsMenu: showAgentsMenu, agents: footerAgents, selectedAgentId: selectedAgentId ?? footerAgents[0]?.id, onSelectAgent: onSelectAgent, hasConfigData: !!configQuery.data, hasSkillsData: !!skillsQuery.data,
3871
3782
  // So the bar can tell "not here yet" from "not coming": a chat whose
3872
3783
  // agent has no config endpoint waits for ever otherwise.
3873
3784
  configLoading: configQuery.isLoading, models: offeredModels,
@@ -0,0 +1,2 @@
1
+ import type { ChatMessage } from '../../types/messages';
2
+ export declare function extractChatMessagesFromFullContext(fullContext: Record<string, unknown> | null): ChatMessage[];
@@ -0,0 +1,110 @@
1
+ /*
2
+ * Copyright (c) 2025-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * A chat's conversation as the runtime's agent holds it (`fullContext`, from
7
+ * the `agent.snapshot` it sends), drawn as messages — kept out of `ChatBase`
8
+ * so it can be read and tested without rendering a chat.
9
+ *
10
+ * A shipped application's page opens on this conversation, so a turn a
11
+ * personal agent asked over PAP or PACT is drawn through `askedOf`: the
12
+ * preamble the runtime told the application (`<asked-for>…</asked-for>`) is
13
+ * kept in the record for the builder and never shown to the person.
14
+ */
15
+ import { askedOf } from '../../apps/apps/records';
16
+ export function extractChatMessagesFromFullContext(fullContext) {
17
+ if (!fullContext) {
18
+ return [];
19
+ }
20
+ const rawMessages = Array.isArray(fullContext.messages)
21
+ ? fullContext.messages
22
+ : [];
23
+ return rawMessages
24
+ .map((msg, index) => {
25
+ const role = String(msg.role || '').toLowerCase();
26
+ const timestampValue = typeof msg.timestamp === 'string' && msg.timestamp.length > 0
27
+ ? msg.timestamp
28
+ : new Date().toISOString();
29
+ const createdAtRaw = new Date(timestampValue);
30
+ const createdAt = Number.isNaN(createdAtRaw.getTime())
31
+ ? new Date()
32
+ : createdAtRaw;
33
+ const rawContent = typeof msg.content === 'string'
34
+ ? msg.content
35
+ : JSON.stringify(msg.content ?? '');
36
+ const isToolCall = Boolean(msg.isToolCall);
37
+ const isToolResult = Boolean(msg.isToolResult);
38
+ const toolCallId = typeof msg.toolCallId === 'string' && msg.toolCallId.length > 0
39
+ ? msg.toolCallId
40
+ : undefined;
41
+ const toolName = typeof msg.toolName === 'string' && msg.toolName.length > 0
42
+ ? msg.toolName
43
+ : undefined;
44
+ // Tool-call turns arrive as assistant messages whose `content` is the
45
+ // JSON-encoded tool arguments. Reconstruct a structured `toolCalls`
46
+ // entry so it renders as a tool card (matching the live stream) instead
47
+ // of leaking raw JSON into the transcript.
48
+ if (isToolCall) {
49
+ let args = {};
50
+ if (rawContent) {
51
+ try {
52
+ const parsed = JSON.parse(rawContent);
53
+ if (parsed &&
54
+ typeof parsed === 'object' &&
55
+ !Array.isArray(parsed)) {
56
+ args = parsed;
57
+ }
58
+ }
59
+ catch {
60
+ args = {};
61
+ }
62
+ }
63
+ const resolvedToolCallId = toolCallId || `history-tc-${index}`;
64
+ return {
65
+ id: `history-toolcall-${index}-${timestampValue}`,
66
+ role: 'assistant',
67
+ content: '',
68
+ createdAt,
69
+ toolCalls: [
70
+ {
71
+ type: 'tool-call',
72
+ toolCallId: resolvedToolCallId,
73
+ toolName: toolName || 'tool',
74
+ args,
75
+ status: 'completed',
76
+ },
77
+ ],
78
+ };
79
+ }
80
+ // Tool results arrive as `role: 'tool'` messages; keep them (with the
81
+ // matching toolCallId) so `convertHistoryToDisplayItems` can merge the
82
+ // result into its tool card.
83
+ if (isToolResult || role === 'tool') {
84
+ return {
85
+ id: `history-toolresult-${index}-${timestampValue}`,
86
+ role: 'tool',
87
+ content: rawContent,
88
+ createdAt,
89
+ metadata: {
90
+ toolCallId,
91
+ toolName,
92
+ },
93
+ };
94
+ }
95
+ // Only hydrate conversational turns in the visible history.
96
+ // System messages may contain internal prompts and metadata.
97
+ if (role !== 'user' && role !== 'assistant') {
98
+ return null;
99
+ }
100
+ // A turn a personal agent asked: what the runtime told the application
101
+ // before the person's words is never shown to them (decided 2026-10-14).
102
+ return {
103
+ id: `history-${role}-${index}-${timestampValue}`,
104
+ role,
105
+ content: role === 'user' ? askedOf(rawContent) : rawContent,
106
+ createdAt,
107
+ };
108
+ })
109
+ .filter((m) => m !== null);
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/agent-runtimes",
3
- "version": "1.5.15",
3
+ "version": "1.5.16",
4
4
  "type": "module",
5
5
  "workspaces": [
6
6
  ".",