@datalayer/agent-runtimes 1.3.34 → 1.3.36
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/lib/chat/ChatFloating.d.ts +8 -2
- package/lib/chat/ChatFloating.js +2 -2
- package/lib/chat/base/ChatBase.js +10 -6
- package/lib/chat/display/EmptyState.js +8 -1
- package/lib/chat/presence/Presence.d.ts +15 -1
- package/lib/chat/presence/Presence.js +0 -13
- package/lib/components/a2ui/datalayer/Chart.d.ts +132 -0
- package/lib/components/a2ui/datalayer/Chart.js +173 -0
- package/lib/components/a2ui/datalayer/Chat.d.ts +21 -0
- package/lib/components/a2ui/datalayer/Chat.js +116 -0
- package/lib/components/a2ui/datalayer/Evidence.d.ts +22 -0
- package/lib/components/a2ui/datalayer/Evidence.js +73 -0
- package/lib/components/a2ui/datalayer/FileUpload.d.ts +22 -0
- package/lib/components/a2ui/datalayer/FileUpload.js +107 -0
- package/lib/components/a2ui/datalayer/Form.d.ts +12 -0
- package/lib/components/a2ui/datalayer/Form.js +46 -0
- package/lib/components/a2ui/datalayer/Table.d.ts +20 -0
- package/lib/components/a2ui/datalayer/Table.js +103 -0
- package/lib/components/a2ui/datalayer/drawn.d.ts +10 -0
- package/lib/components/a2ui/datalayer/drawn.js +20 -0
- package/lib/components/a2ui/datalayer/implementation.d.ts +17 -0
- package/lib/components/a2ui/datalayer/implementation.js +10 -0
- package/lib/components/a2ui/datalayer/index.d.ts +23 -0
- package/lib/components/a2ui/datalayer/index.js +44 -0
- package/lib/components/a2ui/datalayer/parts.d.ts +32 -0
- package/lib/components/a2ui/datalayer/parts.js +44 -0
- package/lib/components/a2ui/datalayer/schema.d.ts +34 -0
- package/lib/components/a2ui/datalayer/schema.js +108 -0
- package/lib/components/a2ui/index.d.ts +1 -0
- package/lib/components/a2ui/index.js +1 -0
- package/lib/components/a2ui/visibleWhen.d.ts +4 -2
- package/lib/components/a2ui/visibleWhen.js +6 -3
- package/lib/loop/apps/AppRenderer.d.ts +8 -0
- package/lib/loop/apps/AppRenderer.js +21 -1
- package/lib/loop/apps/appspec.d.ts +9 -2
- package/lib/loop/apps/appspec.js +14 -8
- package/lib/loop/apps/checks.js +4 -3
- package/lib/loop/embed/AppEmbed.d.ts +16 -1
- package/lib/loop/embed/AppEmbed.js +38 -9
- package/lib/loop/embed/LoopEmbed.js +2 -0
- package/lib/loop/embed/element.js +10 -1
- package/lib/loop/embed/embedTheme.d.ts +12 -0
- package/lib/loop/embed/embedTheme.js +15 -0
- package/lib/loop/plugins/assistant/index.d.ts +38 -0
- package/lib/loop/plugins/assistant/index.js +140 -0
- package/lib/loop/plugins/assistant-characters/index.d.ts +35 -1
- package/lib/loop/plugins/assistant-characters/index.js +45 -2
- package/lib/loop/plugins/chat/ChatView.js +21 -9
- package/lib/loop/plugins/chat/index.d.ts +6 -0
- package/lib/loop/presets.d.ts +3 -0
- package/lib/loop/presets.js +2 -1
- package/lib/specs/appspecSchema.js +5 -8
- package/lib/types/agentspecs.d.ts +10 -4
- package/lib/types/chat.d.ts +11 -0
- package/package.json +2 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState } from 'react';
|
|
3
|
+
import { definePlugin, signal } from '@datalayer/reactor';
|
|
4
|
+
import { useContributions, useSignalValue } from '@datalayer/reactor/react';
|
|
5
|
+
import { Text } from '@primer/react';
|
|
6
|
+
import { Box } from '@datalayer/primer-addons';
|
|
7
|
+
import { pageLayoutPanelOpen, togglePagePanel, } from '@datalayer/primer-addons/lib/reactor';
|
|
8
|
+
import { AssistantStage } from '../../../chat/assistant/AssistantStage';
|
|
9
|
+
import { assistantStateOf, keepAway, keptAway, latestSaying, } from '../../../chat/assistant/state';
|
|
10
|
+
import { useViewportDrag } from '../../../chat/useViewportDrag';
|
|
11
|
+
import { LoopAssistantCharacter, LoopChatLayout, LoopChatTurn, LoopSlots, } from '../../core';
|
|
12
|
+
import { assistantCharacterFor } from '../assistant-characters';
|
|
13
|
+
export const LOOP_ASSISTANT_PLUGIN_NAME = '@datalayer/loop-plugin-assistant';
|
|
14
|
+
/** The character's size, in pixels, as the chat's own assistant. */
|
|
15
|
+
const SIZE = 88;
|
|
16
|
+
/* A signal to read when no chat contributed a turn: the hook needs one. */
|
|
17
|
+
const NO_TURN = signal({ id: 0, status: 'idle' });
|
|
18
|
+
/**
|
|
19
|
+
* What the character acts out, from the workspace's chat turn (T-22): it
|
|
20
|
+
* thinks while the agent thinks, works while a tool runs (the turn's
|
|
21
|
+
* `activity`), speaks while the answer is written, and idles otherwise.
|
|
22
|
+
*/
|
|
23
|
+
export function assistantStateOfTurn(turn, moment = {}) {
|
|
24
|
+
const busy = turn.status === 'thinking' || turn.status === 'streaming';
|
|
25
|
+
const presence = !busy
|
|
26
|
+
? 'idle'
|
|
27
|
+
: turn.activity
|
|
28
|
+
? 'working'
|
|
29
|
+
: 'thinking';
|
|
30
|
+
return assistantStateOf(presence, {
|
|
31
|
+
...moment,
|
|
32
|
+
speaking: turn.status === 'streaming' && !turn.activity,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/** Take the person to the workspace's composer. */
|
|
36
|
+
function focusComposer() {
|
|
37
|
+
const composer = document.querySelector('[data-chat-composer] textarea, [data-chat-composer] [contenteditable="true"]');
|
|
38
|
+
composer?.focus();
|
|
39
|
+
}
|
|
40
|
+
export function LoopAssistant({ app, person, }) {
|
|
41
|
+
const contributed = useContributions(LoopAssistantCharacter).map(entry => entry.value);
|
|
42
|
+
const chosen = assistantCharacterFor(contributed, { app, person });
|
|
43
|
+
const turnEntries = useContributions(LoopChatTurn);
|
|
44
|
+
const turn = useSignalValue(turnEntries[0]?.value.turn ?? NO_TURN);
|
|
45
|
+
// On a page layout the conversation is its side panel; elsewhere it is
|
|
46
|
+
// always on screen.
|
|
47
|
+
const onPageLayout = useContributions(LoopChatLayout).some(entry => entry.value.id === 'page-layout');
|
|
48
|
+
const panelOpen = useSignalValue(pageLayoutPanelOpen);
|
|
49
|
+
const open = onPageLayout && panelOpen;
|
|
50
|
+
const [away, setAway] = useState(keptAway);
|
|
51
|
+
const [arriving, setArriving] = useState(true);
|
|
52
|
+
const [leaving, setLeaving] = useState(false);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const timer = setTimeout(() => setArriving(false), 1800);
|
|
55
|
+
return () => clearTimeout(timer);
|
|
56
|
+
}, []);
|
|
57
|
+
/* The agent's newest words, said in the balloon until heard (T-23). */
|
|
58
|
+
const saying = turn.assistant
|
|
59
|
+
? latestSaying([
|
|
60
|
+
{ id: String(turn.id), role: 'assistant', content: turn.assistant },
|
|
61
|
+
])
|
|
62
|
+
: undefined;
|
|
63
|
+
const [heardId, setHeardId] = useState();
|
|
64
|
+
const [fresh, setFresh] = useState(false);
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (!saying || open) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
setFresh(true);
|
|
70
|
+
const timer = setTimeout(() => setFresh(false), 12000);
|
|
71
|
+
return () => clearTimeout(timer);
|
|
72
|
+
}, [saying?.id, saying?.text, open]);
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (open && saying) {
|
|
75
|
+
setHeardId(saying.id);
|
|
76
|
+
}
|
|
77
|
+
}, [open, saying?.id]);
|
|
78
|
+
const stageRef = useRef(null);
|
|
79
|
+
const drag = useViewportDrag(stageRef, { whole: true });
|
|
80
|
+
if (away !== 'none') {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
if ('problem' in chosen) {
|
|
84
|
+
// Not replaced by another: what was named is said, where it would stand.
|
|
85
|
+
return (_jsx(Box, { role: "status", "data-assistant-problem": chosen.saidBy, sx: {
|
|
86
|
+
position: 'fixed',
|
|
87
|
+
right: 24,
|
|
88
|
+
bottom: 24,
|
|
89
|
+
zIndex: 1000,
|
|
90
|
+
maxWidth: 320,
|
|
91
|
+
p: 2,
|
|
92
|
+
borderRadius: 2,
|
|
93
|
+
border: '1px solid',
|
|
94
|
+
borderColor: 'attention.muted',
|
|
95
|
+
bg: 'attention.subtle',
|
|
96
|
+
boxShadow: 'shadow.medium',
|
|
97
|
+
}, children: _jsx(Text, { sx: { fontSize: 1 }, children: chosen.problem }) }));
|
|
98
|
+
}
|
|
99
|
+
const state = assistantStateOfTurn(turn, { arriving, leaving });
|
|
100
|
+
const unheard = !!saying && saying.id !== heardId;
|
|
101
|
+
const balloon = unheard && saying
|
|
102
|
+
? { text: saying.text, more: saying.more }
|
|
103
|
+
: { text: 'Click me to open the conversation.' };
|
|
104
|
+
const insist = state === 'greeting' || (unheard && (state === 'speaking' || fresh));
|
|
105
|
+
const onToggle = () => {
|
|
106
|
+
if (onPageLayout) {
|
|
107
|
+
togglePagePanel();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
focusComposer();
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
const onDismiss = (next) => {
|
|
114
|
+
keepAway(next);
|
|
115
|
+
setLeaving(true);
|
|
116
|
+
setTimeout(() => {
|
|
117
|
+
setLeaving(false);
|
|
118
|
+
setAway(next);
|
|
119
|
+
}, 600);
|
|
120
|
+
};
|
|
121
|
+
return (_jsx(AssistantStage, { character: chosen.character, state: state, size: SIZE, place: drag.position
|
|
122
|
+
? { left: `${drag.position.left}px`, top: `${drag.position.top}px` }
|
|
123
|
+
: { right: '24px', bottom: '24px' }, stageRef: stageRef, onDragStart: drag.onHandlePointerDown, open: open, onToggle: onToggle, balloon: balloon, insist: insist, onDismiss: onDismiss }));
|
|
124
|
+
}
|
|
125
|
+
export const LoopAssistantPlugin = definePlugin({
|
|
126
|
+
name: LOOP_ASSISTANT_PLUGIN_NAME,
|
|
127
|
+
config: { app: undefined, person: undefined },
|
|
128
|
+
displayName: 'Floating assistant',
|
|
129
|
+
description: 'The chat as a character on the page: the application’s own, or the one the person chose, from the characters the enabled plugins contribute.',
|
|
130
|
+
octicon: 'paperclip',
|
|
131
|
+
build: ({ config }) => {
|
|
132
|
+
const Configured = () => (_jsx(LoopAssistant, { app: config.app, person: config.person }));
|
|
133
|
+
return {
|
|
134
|
+
components: [
|
|
135
|
+
{ id: 'loop-assistant', slot: LoopSlots.root, Component: Configured },
|
|
136
|
+
],
|
|
137
|
+
};
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
export default LoopAssistantPlugin;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*
|
|
6
6
|
* @module loop/plugins/assistant-characters
|
|
7
7
|
*/
|
|
8
|
-
import { type ReactorPlugin } from '@datalayer/reactor';
|
|
8
|
+
import { type PluginRef, type ReactorPlugin } from '@datalayer/reactor';
|
|
9
9
|
import { type AssistantCharacter } from '../../../chat/assistant/characters';
|
|
10
10
|
import type { AssistantCharacterData } from '../../../chat/assistant/formats/types';
|
|
11
11
|
import { LoopAssistantCharacter, type AssistantCharacterContribution } from '../../core';
|
|
@@ -22,9 +22,43 @@ export declare function assistantCharactersOf(reactor: ContributionsReader): {
|
|
|
22
22
|
id: string;
|
|
23
23
|
character: AssistantCharacter | AssistantCharacterData;
|
|
24
24
|
}[];
|
|
25
|
+
/**
|
|
26
|
+
* What these plugins contribute, enabled on their own: for a host with no
|
|
27
|
+
* workspace to read them from — the embed's floating assistant (D-07) —
|
|
28
|
+
* which enables Datalayer's characters and whatever plugins it is given.
|
|
29
|
+
*/
|
|
30
|
+
export declare function assistantCharactersFrom(plugins: PluginRef[]): {
|
|
31
|
+
id: string;
|
|
32
|
+
character: AssistantCharacter | AssistantCharacterData;
|
|
33
|
+
}[];
|
|
25
34
|
/**
|
|
26
35
|
* The character an application names, from what is contributed; an id no
|
|
27
36
|
* enabled plugin contributes is an error that says which ids there are.
|
|
28
37
|
*/
|
|
29
38
|
export declare function assistantCharacterNamed(reactor: ContributionsReader, id: string): AssistantCharacter | AssistantCharacterData;
|
|
39
|
+
/** Whose choice the character drawn is (T-24). */
|
|
40
|
+
export type AssistantCharacterSaidBy = 'app' | 'person' | 'default';
|
|
41
|
+
/** The character drawn, or why none can be. */
|
|
42
|
+
export type AssistantCharacterChosen = {
|
|
43
|
+
id: string;
|
|
44
|
+
character: AssistantCharacter | AssistantCharacterData;
|
|
45
|
+
saidBy: AssistantCharacterSaidBy;
|
|
46
|
+
} | {
|
|
47
|
+
problem: string;
|
|
48
|
+
saidBy: AssistantCharacterSaidBy;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* The character the floating assistant draws on a page (T-24): the one the
|
|
52
|
+
* application's Appspec names (`interface.assistant`), which wins; else the
|
|
53
|
+
* one the person chose in their settings; else the paper clip — each looked
|
|
54
|
+
* up in what the enabled plugins contribute. An id no enabled plugin gives
|
|
55
|
+
* is not replaced by another: it is said, with the ids there are.
|
|
56
|
+
*/
|
|
57
|
+
export declare function assistantCharacterFor(contributed: ReadonlyArray<{
|
|
58
|
+
id: string;
|
|
59
|
+
character: AssistantCharacter | AssistantCharacterData;
|
|
60
|
+
}>, choice: {
|
|
61
|
+
app?: string;
|
|
62
|
+
person?: string;
|
|
63
|
+
}): AssistantCharacterChosen;
|
|
30
64
|
export default AssistantCharactersPlugin;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* @module loop/plugins/assistant-characters
|
|
11
11
|
*/
|
|
12
|
-
import { contribution, definePlugin, } from '@datalayer/reactor';
|
|
13
|
-
import { ASSISTANT_CHARACTERS, } from '../../../chat/assistant/characters';
|
|
12
|
+
import { buildReactorFromPlugins, contribution, definePlugin, } from '@datalayer/reactor';
|
|
13
|
+
import { ASSISTANT_CHARACTERS, DEFAULT_ASSISTANT_CHARACTER, } from '../../../chat/assistant/characters';
|
|
14
14
|
import { LoopAssistantCharacter, } from '../../core';
|
|
15
15
|
export const ASSISTANT_CHARACTERS_PLUGIN_NAME = '@datalayer/loop-plugin-assistant-characters';
|
|
16
16
|
export const AssistantCharactersPlugin = definePlugin({
|
|
@@ -26,6 +26,21 @@ export function assistantCharactersOf(reactor) {
|
|
|
26
26
|
.getContributions(LoopAssistantCharacter)
|
|
27
27
|
.map(entry => entry.value);
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* What these plugins contribute, enabled on their own: for a host with no
|
|
31
|
+
* workspace to read them from — the embed's floating assistant (D-07) —
|
|
32
|
+
* which enables Datalayer's characters and whatever plugins it is given.
|
|
33
|
+
*/
|
|
34
|
+
export function assistantCharactersFrom(plugins) {
|
|
35
|
+
const reactor = buildReactorFromPlugins(plugins);
|
|
36
|
+
reactor.start();
|
|
37
|
+
try {
|
|
38
|
+
return assistantCharactersOf(reactor);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
reactor.stop();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
29
44
|
/**
|
|
30
45
|
* The character an application names, from what is contributed; an id no
|
|
31
46
|
* enabled plugin contributes is an error that says which ids there are.
|
|
@@ -38,4 +53,32 @@ export function assistantCharacterNamed(reactor, id) {
|
|
|
38
53
|
}
|
|
39
54
|
return found.character;
|
|
40
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The character the floating assistant draws on a page (T-24): the one the
|
|
58
|
+
* application's Appspec names (`interface.assistant`), which wins; else the
|
|
59
|
+
* one the person chose in their settings; else the paper clip — each looked
|
|
60
|
+
* up in what the enabled plugins contribute. An id no enabled plugin gives
|
|
61
|
+
* is not replaced by another: it is said, with the ids there are.
|
|
62
|
+
*/
|
|
63
|
+
export function assistantCharacterFor(contributed, choice) {
|
|
64
|
+
const saidBy = choice.app
|
|
65
|
+
? 'app'
|
|
66
|
+
: choice.person
|
|
67
|
+
? 'person'
|
|
68
|
+
: 'default';
|
|
69
|
+
const id = choice.app ?? choice.person ?? DEFAULT_ASSISTANT_CHARACTER;
|
|
70
|
+
const found = contributed.find(entry => entry.id === id);
|
|
71
|
+
if (found) {
|
|
72
|
+
return { id, character: found.character, saidBy };
|
|
73
|
+
}
|
|
74
|
+
const whose = saidBy === 'app'
|
|
75
|
+
? 'This application names'
|
|
76
|
+
: saidBy === 'person'
|
|
77
|
+
? 'Your settings name'
|
|
78
|
+
: 'The default is';
|
|
79
|
+
return {
|
|
80
|
+
saidBy,
|
|
81
|
+
problem: `${whose} the character “${id}”, which nothing enabled here draws; the characters are ${contributed.map(entry => entry.id).join(', ') || 'none'}.`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
41
84
|
export default AssistantCharactersPlugin;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { Box, IconButton, SegmentedControl, Text, Truncate, } from '@primer/react';
|
|
4
|
-
import { useColorPalette } from '@datalayer/primer-addons';
|
|
4
|
+
import { loopShapeVars, useColorPalette } from '@datalayer/primer-addons';
|
|
5
5
|
import { ScreenFullIcon, ScreenNormalIcon } from '@primer/octicons-react';
|
|
6
6
|
import { computed, signal } from '@datalayer/reactor';
|
|
7
7
|
/* A signal to read when the layout publishes no live stance: the hook needs one. */
|
|
@@ -54,6 +54,11 @@ const NO_SURFACE = '';
|
|
|
54
54
|
* small control reads as an error state, and this one is an invitation.
|
|
55
55
|
*/
|
|
56
56
|
const FULLSCREEN_HINT_PERIOD_MS = 1400;
|
|
57
|
+
/**
|
|
58
|
+
* An application's face in the empty state: the page's size of the three a
|
|
59
|
+
* face is drawn at (LOOP T-19, `--loop-face-large`, 72px).
|
|
60
|
+
*/
|
|
61
|
+
const FACE_LARGE = parseInt(loopShapeVars['--loop-face-large'], 10);
|
|
57
62
|
export default function ChatView({ workspace }) {
|
|
58
63
|
/* The active theme's own colours. Read from the store rather than a
|
|
59
64
|
provider, so a workspace mounted without the addons theme still gets the
|
|
@@ -1326,7 +1331,14 @@ export default function ChatView({ workspace }) {
|
|
|
1326
1331
|
mentionableAgents: mentionable,
|
|
1327
1332
|
headerContent: inpromptMenu.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.inpromptMenu, props: { workspace } })) : undefined,
|
|
1328
1333
|
footerExtras: promptActions.length > 0 ? (_jsx(ReactorSlot, { slot: LoopSlots.promptAction, props: { workspace } })) : undefined,
|
|
1329
|
-
|
|
1334
|
+
/*
|
|
1335
|
+
An application's conversation keeps none of the session's controls
|
|
1336
|
+
under its composer — the agents, the model, the tools and their servers,
|
|
1337
|
+
the skills (LOOP T-17, T-18): a person using an application is not
|
|
1338
|
+
choosing them, and its builder chose them in its spec. An agent's chat
|
|
1339
|
+
keeps them.
|
|
1340
|
+
*/
|
|
1341
|
+
showAgentsMenu: !presence,
|
|
1330
1342
|
showInlineAgentsMenu: false,
|
|
1331
1343
|
agents: footerAgents,
|
|
1332
1344
|
selectedAgentId: selectedMemberId,
|
|
@@ -1336,18 +1348,18 @@ export default function ChatView({ workspace }) {
|
|
|
1336
1348
|
showTokenUsage: chatExtras.showTokenUsage ?? config?.showTokenUsage ?? true,
|
|
1337
1349
|
showContextRing: true,
|
|
1338
1350
|
agentUsage: contextUsage ?? undefined,
|
|
1339
|
-
showModelSelector:
|
|
1351
|
+
showModelSelector: !presence,
|
|
1340
1352
|
models: offeredModels,
|
|
1341
1353
|
decisions,
|
|
1342
1354
|
selectedModel: activeModel,
|
|
1343
1355
|
onModelSelect: model => void selectModel(model),
|
|
1344
|
-
showToolsMenu:
|
|
1356
|
+
showToolsMenu: !presence,
|
|
1345
1357
|
availableTools: offeredTools,
|
|
1346
1358
|
mcpServers: configQuery.data?.mcpServers ?? [],
|
|
1347
1359
|
// Live from the host example, when one feeds it: the footer's MCP status
|
|
1348
1360
|
// indicator. (Codemode surfaces through the toggle below, not a blob.)
|
|
1349
1361
|
mcpStatusData: chatExtras.mcpStatusData ?? undefined,
|
|
1350
|
-
showSkillsMenu:
|
|
1362
|
+
showSkillsMenu: !presence,
|
|
1351
1363
|
skills: skillsQuery.data?.skills ?? [],
|
|
1352
1364
|
skillsLoading: skillsQuery.isLoading,
|
|
1353
1365
|
enabledSkills,
|
|
@@ -1444,7 +1456,7 @@ export default function ChatView({ workspace }) {
|
|
|
1444
1456
|
// The theme it wears, when the host names one (an application's).
|
|
1445
1457
|
, {
|
|
1446
1458
|
// The theme it wears, when the host names one (an application's).
|
|
1447
|
-
themeVariant: config?.themeVariant,
|
|
1459
|
+
themeVariant: config?.themeVariant, themeOverrides: config?.themeOverrides,
|
|
1448
1460
|
// The header says why, beside the title, for the same reason the
|
|
1449
1461
|
// placeholder does: a dead control with no explanation is worse
|
|
1450
1462
|
// than an absent one.
|
|
@@ -1483,9 +1495,9 @@ export default function ChatView({ workspace }) {
|
|
|
1483
1495
|
// An application says what it is doing, in plain words, beside
|
|
1484
1496
|
// its name (T-08); an agent's chat keeps its header as it is.
|
|
1485
1497
|
headerContent: presence ? _jsx(PresenceLine, { state: presenceNow }) : undefined, emptyState: {
|
|
1486
|
-
// An application's own face,
|
|
1487
|
-
//
|
|
1488
|
-
icon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize:
|
|
1498
|
+
// An application's own face, at the page's size of the three
|
|
1499
|
+
// (LOOP T-08, T-19): the thing a person's eye lands on first.
|
|
1500
|
+
icon: presence?.face ? (_jsx("span", { "aria-hidden": true, style: { fontSize: FACE_LARGE, lineHeight: 1 }, children: presence.face })) : (_jsx(BrandIcon, { size: 48 })),
|
|
1489
1501
|
// `ChatEmptyState` reads its heading from here and nowhere
|
|
1490
1502
|
// else — the `title` above reaches the header only — so
|
|
1491
1503
|
// without this the agent introduced itself as "Start a
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PresenceState } from '../../../chat/presence/presenceStatus';
|
|
2
|
+
import type { ThemeOverrides } from '../../../types/chat';
|
|
2
3
|
/** What a host may set on the chat. */
|
|
3
4
|
/** Who is answering, as the person meets them. */
|
|
4
5
|
export interface ChatPresence {
|
|
@@ -22,6 +23,11 @@ export type ChatPluginConfig = {
|
|
|
22
23
|
* the theme's bubbles, pills and colours whatever the page around it wears.
|
|
23
24
|
*/
|
|
24
25
|
themeVariant?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Laid over that theme, by mode: an application's accent (LOOP T-05), so
|
|
28
|
+
* that its bubbles and its one button wear its colour and not the theme's.
|
|
29
|
+
*/
|
|
30
|
+
themeOverrides?: ThemeOverrides;
|
|
25
31
|
/**
|
|
26
32
|
* Who is answering, as the person meets them (LOOP T-08): a name, a face
|
|
27
33
|
* and a welcome, in place of the agent's id and a generic icon. An
|
package/lib/loop/presets.d.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { type PluginRef } from '@datalayer/reactor';
|
|
|
23
23
|
import { type AgentsConfig, type SandboxTarget } from './plugins/agents';
|
|
24
24
|
import { type ChatPluginConfig, type ChatPresence } from './plugins/chat';
|
|
25
25
|
import { type PageSize } from './plugins/page-layout';
|
|
26
|
+
import type { ThemeOverrides } from '../types/chat';
|
|
26
27
|
export type LoopPresetOptions = {
|
|
27
28
|
/** Where the agent runtimes service is. */
|
|
28
29
|
serverUrl?: string;
|
|
@@ -140,6 +141,8 @@ export type LoopPresetOptions = {
|
|
|
140
141
|
targetFixed?: boolean;
|
|
141
142
|
/** The theme the conversation wears, by name; the person's unless said. */
|
|
142
143
|
themeVariant?: string;
|
|
144
|
+
/** Laid over that theme, by mode: an application's accent (LOOP T-05). */
|
|
145
|
+
themeOverrides?: ThemeOverrides;
|
|
143
146
|
/** Who is answering, as the person meets them (LOOP T-08). */
|
|
144
147
|
presence?: ChatPresence;
|
|
145
148
|
/** Whether the counters are shown under the prompt; on unless said. */
|
package/lib/loop/presets.js
CHANGED
|
@@ -52,7 +52,7 @@ import { LoopPageLayoutPlugin } from './plugins/page-layout';
|
|
|
52
52
|
* be installed or signed into, and the switches a demonstration wants are off.
|
|
53
53
|
*/
|
|
54
54
|
export function loopPlugins(options = {}) {
|
|
55
|
-
const { serverUrl, target = 'browser', defaultEditor: askedEditor, showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutArrangement = 'page', pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
55
|
+
const { serverUrl, target = 'browser', defaultEditor: askedEditor, showViewSelector = true, hideChatHeader = false, chatHeaderButtons = false, promptPlacement, autoFocusPrompt = true, suggestionLabels = true, fullScreenTopOffset = 0, showAgentVariants = false, agentSummary = true, teamId, teamPicker = true, pageLayout = false, pageLayoutArrangement = 'page', pageLayoutTurnPanel = 'below', pageLayoutPrompt = 'docked', pageLayoutPromptAnchor = 'top', pageLayoutSize, pageLayoutTurnPanelFooter = 'full', localAgent, localAgentSpec, datalayerAgentSpecId, datalayerCreatePayload, targetFixed, themeVariant, themeOverrides, presence, showTokenUsage, floatingPrompt = false, editorSelector = false, editors = true, graph = false, commandPalette = false, pluginsPanel = false, windowFrame = false, firstPromptHook, } = options;
|
|
56
56
|
const defaultEditor = askedEditor ?? (editors ? 'notebook' : 'none');
|
|
57
57
|
return [
|
|
58
58
|
// The chat owns the editor beside it, so which one opens is its
|
|
@@ -70,6 +70,7 @@ export function loopPlugins(options = {}) {
|
|
|
70
70
|
suggestionLabels,
|
|
71
71
|
fullScreenTopOffset,
|
|
72
72
|
themeVariant,
|
|
73
|
+
themeOverrides,
|
|
73
74
|
presence,
|
|
74
75
|
showTokenUsage,
|
|
75
76
|
}),
|
|
@@ -314,14 +314,17 @@ export const APPSPEC_SCHEMA = {
|
|
|
314
314
|
assistant: {
|
|
315
315
|
anyOf: [
|
|
316
316
|
{
|
|
317
|
-
|
|
317
|
+
maxLength: 64,
|
|
318
|
+
pattern: '^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$',
|
|
319
|
+
type: 'string',
|
|
318
320
|
},
|
|
319
321
|
{
|
|
320
322
|
type: 'null',
|
|
321
323
|
},
|
|
322
324
|
],
|
|
323
325
|
default: null,
|
|
324
|
-
description:
|
|
326
|
+
description: "The character its floating assistant shows, by the id a plugin contributes it under (lowercase letters and digits, words joined by a hyphen): Datalayer's are `paperclip`, `wizard`, `cat` and `eyes`. The paper clip when unsaid; an id no enabled plugin contributes is refused where the plugins are known, the runtime and the page. Said here, it wins over a person's own choice in their settings",
|
|
327
|
+
title: 'Assistant',
|
|
325
328
|
},
|
|
326
329
|
},
|
|
327
330
|
title: 'AppInterface',
|
|
@@ -677,12 +680,6 @@ export const APPSPEC_SCHEMA = {
|
|
|
677
680
|
title: 'AppTrigger',
|
|
678
681
|
type: 'object',
|
|
679
682
|
},
|
|
680
|
-
AssistantCharacter: {
|
|
681
|
-
description: "The character an application's floating assistant shows (LOOP T-24):\none of those a UI plugin contributes. Datalayer's are these four.",
|
|
682
|
-
enum: ['paperclip', 'wizard', 'cat', 'eyes'],
|
|
683
|
-
title: 'AssistantCharacter',
|
|
684
|
-
type: 'string',
|
|
685
|
-
},
|
|
686
683
|
Behaviour: {
|
|
687
684
|
description: 'What an application does when it meets an action: the four a person chooses from.',
|
|
688
685
|
enum: ['do_it', 'if_asked', 'ask_first', 'leave_to_me'],
|
|
@@ -384,10 +384,13 @@ export type AppLayout = 'chat' | 'page' | 'split';
|
|
|
384
384
|
/** The one colour of an application; everything else is neutral. */
|
|
385
385
|
export type AppAccent = 'green' | 'rose' | 'sky' | 'lime' | 'sun' | 'violet';
|
|
386
386
|
/**
|
|
387
|
-
* The character an application's floating assistant shows (LOOP T-24):
|
|
388
|
-
*
|
|
387
|
+
* The character an application's floating assistant shows (LOOP T-24): the id
|
|
388
|
+
* an enabled plugin contributes it under to `loop.assistant.character` —
|
|
389
|
+
* Datalayer's are `paperclip`, `wizard`, `cat` and `eyes`. The spec checks
|
|
390
|
+
* its shape (`APP_ASSISTANT_CHARACTER_ID`); whether a plugin gives it is
|
|
391
|
+
* known only where the plugins are.
|
|
389
392
|
*/
|
|
390
|
-
export type AppAssistantCharacter =
|
|
393
|
+
export type AppAssistantCharacter = string;
|
|
391
394
|
/** How an application sits in another product's page (LOOP D-07). */
|
|
392
395
|
export type AppEmbedMode = 'inline' | 'bubble' | 'panel' | 'assistant';
|
|
393
396
|
/** Something an application reaches. */
|
|
@@ -456,7 +459,10 @@ export interface AppInterfaceSpec {
|
|
|
456
459
|
/** The components of the catalog the surface may use. */
|
|
457
460
|
components: string[];
|
|
458
461
|
surface?: AppSurfaceSpec;
|
|
459
|
-
/**
|
|
462
|
+
/**
|
|
463
|
+
* The character its floating assistant shows; the paper clip when unsaid.
|
|
464
|
+
* Said, it wins over the one the person chose in their settings.
|
|
465
|
+
*/
|
|
460
466
|
assistant?: AppAssistantCharacter;
|
|
461
467
|
}
|
|
462
468
|
export interface AppTestCaseSpec {
|
package/lib/types/chat.d.ts
CHANGED
|
@@ -636,6 +636,11 @@ export interface ChatCommonProps {
|
|
|
636
636
|
/**
|
|
637
637
|
* ChatBase props
|
|
638
638
|
*/
|
|
639
|
+
/** Properties laid over a theme's own, by mode. */
|
|
640
|
+
export type ThemeOverrides = {
|
|
641
|
+
light?: Record<string, string>;
|
|
642
|
+
dark?: Record<string, string>;
|
|
643
|
+
};
|
|
639
644
|
export interface ChatBaseProps {
|
|
640
645
|
/**
|
|
641
646
|
* Hands an imperative send function to the host, once the chat is able to
|
|
@@ -838,6 +843,12 @@ export interface ChatBaseProps {
|
|
|
838
843
|
kernelGpu?: string;
|
|
839
844
|
/** Optional theme variant override for companion notebook/document surfaces. */
|
|
840
845
|
themeVariant?: string;
|
|
846
|
+
/**
|
|
847
|
+
* Properties laid over the theme's own, by mode — an application's accent
|
|
848
|
+
* over the theme it wears (LOOP T-05): without them the chat would wear the
|
|
849
|
+
* theme's default accent whatever the page around it set.
|
|
850
|
+
*/
|
|
851
|
+
themeOverrides?: ThemeOverrides;
|
|
841
852
|
/** Optional color mode override for companion notebook/document surfaces. */
|
|
842
853
|
colorMode?: 'light' | 'dark' | 'auto';
|
|
843
854
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datalayer/agent-runtimes",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.36",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"workspaces": [
|
|
6
6
|
".",
|
|
@@ -213,6 +213,7 @@
|
|
|
213
213
|
"@radix-ui/react-toggle-group": "^1.1.11",
|
|
214
214
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
215
215
|
"@radix-ui/react-use-controllable-state": "^1.2.2",
|
|
216
|
+
"@rjsf/validator-ajv8": "^5.24.13",
|
|
216
217
|
"@styled-system/css": "^5.1.5",
|
|
217
218
|
"@tailwindcss/vite": "^4.1.13",
|
|
218
219
|
"@tanstack/react-query": "^5.90.6",
|