@dxos/plugin-assistant 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PLUGIN.mdl +25 -0
- package/dist/lib/AssistantCapabilities.mjs +8 -1
- package/dist/lib/AssistantCapabilities.mjs.map +1 -1
- package/dist/lib/Settings.mjs +4 -0
- package/dist/lib/Settings.mjs.map +1 -1
- package/dist/lib/TurnReviewSkill.mjs +55 -0
- package/dist/lib/TurnReviewSkill.mjs.map +1 -0
- package/dist/lib/chunk-AgentArticle.mjs +1 -1
- package/dist/lib/chunk-AssistantSettings.mjs +1 -1
- package/dist/lib/chunk-AssistantSettings.mjs.map +1 -1
- package/dist/lib/chunk-Chat.mjs +322 -174
- package/dist/lib/chunk-Chat.mjs.map +1 -1
- package/dist/lib/chunk-ChatArticle.mjs +14 -7
- package/dist/lib/chunk-ChatArticle.mjs.map +1 -1
- package/dist/lib/chunk-ChatDialog.mjs +5 -4
- package/dist/lib/chunk-ChatDialog.mjs.map +1 -1
- package/dist/lib/chunk-IntegrationPrompt.mjs +1 -1
- package/dist/lib/chunk-PLUGIN.mjs +1 -1
- package/dist/lib/chunk-PluginUrlPrompt.mjs +1 -1
- package/dist/lib/chunk-QuestionSurface.mjs +1 -1
- package/dist/lib/chunk-SpaceHomePrompt.mjs +116 -60
- package/dist/lib/chunk-SpaceHomePrompt.mjs.map +1 -1
- package/dist/lib/chunk-TriggerStatus.mjs +1 -1
- package/dist/lib/chunk-agent-service.mjs +3 -1
- package/dist/lib/chunk-agent-service.mjs.map +1 -1
- package/dist/lib/chunk-analyze-turn.mjs +220 -0
- package/dist/lib/chunk-analyze-turn.mjs.map +1 -0
- package/dist/lib/{chunk-processor.mjs → chunk-chat-model.mjs} +95 -43
- package/dist/lib/chunk-chat-model.mjs.map +1 -0
- package/dist/lib/chunk-context.mjs.map +1 -1
- package/dist/lib/chunk-create-chat.mjs +5 -19
- package/dist/lib/chunk-create-chat.mjs.map +1 -1
- package/dist/lib/chunk-create-object.mjs +1 -1
- package/dist/lib/chunk-default-skills.mjs +28 -0
- package/dist/lib/chunk-default-skills.mjs.map +1 -0
- package/dist/lib/chunk-generate-home-suggestions.mjs +8 -6
- package/dist/lib/chunk-generate-home-suggestions.mjs.map +1 -1
- package/dist/lib/chunk-presets.mjs +1 -1
- package/dist/lib/chunk-presets.mjs.map +1 -1
- package/dist/lib/chunk-question-resumer.mjs +1 -1
- package/dist/lib/chunk-react-surface.mjs +1 -1
- package/dist/lib/chunk-run-prompt-in-chat.mjs +2 -1
- package/dist/lib/chunk-run-prompt-in-chat.mjs.map +1 -1
- package/dist/lib/chunk-run-prompt-in-new-chat.mjs +1 -1
- package/dist/lib/chunk-schema-defs.mjs +2 -2
- package/dist/lib/chunk-skill-definition.mjs +3 -2
- package/dist/lib/chunk-skill-definition.mjs.map +1 -1
- package/dist/lib/components.mjs +13 -13
- package/dist/lib/components.mjs.map +1 -1
- package/dist/lib/hooks.mjs +40 -31
- package/dist/lib/hooks.mjs.map +1 -1
- package/dist/lib/operations.mjs +3 -1
- package/dist/lib/operations.mjs.map +1 -1
- package/dist/lib/skills.mjs +2 -1
- package/dist/lib/translations.mjs +4 -2
- package/dist/lib/translations.mjs.map +1 -1
- package/dist/types/src/Chat.d.ts +1 -1
- package/dist/types/src/capabilities/agent-service.d.ts.map +1 -1
- package/dist/types/src/capabilities/skill-definition.d.ts.map +1 -1
- package/dist/types/src/{processor/processor.d.ts → chat-model/chat-model.d.ts} +24 -22
- package/dist/types/src/chat-model/chat-model.d.ts.map +1 -0
- package/dist/types/src/chat-model/chat-model.edge.test.d.ts +2 -0
- package/dist/types/src/chat-model/chat-model.edge.test.d.ts.map +1 -0
- package/dist/types/src/chat-model/chat-model.node.test.d.ts +2 -0
- package/dist/types/src/chat-model/chat-model.node.test.d.ts.map +1 -0
- package/dist/types/src/chat-model/chat-model.scripted.node.test.d.ts +2 -0
- package/dist/types/src/chat-model/chat-model.scripted.node.test.d.ts.map +1 -0
- package/dist/types/src/{processor → chat-model}/index.d.ts +1 -1
- package/dist/types/src/chat-model/index.d.ts.map +1 -0
- package/dist/types/src/chat-model/outbox.d.ts.map +1 -0
- package/dist/types/src/chat-model/outbox.test.d.ts.map +1 -0
- package/dist/types/src/chat-model/presets.d.ts.map +1 -0
- package/dist/types/src/chat-model/presets.test.d.ts.map +1 -0
- package/dist/types/src/{processor → chat-model}/prompt.d.ts +2 -2
- package/dist/types/src/chat-model/prompt.d.ts.map +1 -0
- package/dist/types/src/chat-model/prompt.test.d.ts.map +1 -0
- package/dist/types/src/chat-model/streaming.node.test.d.ts.map +1 -0
- package/dist/types/src/chat-model/testing/chat-model-recorder.d.ts +58 -0
- package/dist/types/src/chat-model/testing/chat-model-recorder.d.ts.map +1 -0
- package/dist/types/src/chat-model/testing/harness.d.ts +22 -0
- package/dist/types/src/chat-model/testing/harness.d.ts.map +1 -0
- package/dist/types/src/chat-model/thread.d.ts.map +1 -0
- package/dist/types/src/chat-model/thread.test.d.ts.map +1 -0
- package/dist/types/src/components/Chat/Chat.d.ts +6 -6
- package/dist/types/src/components/Chat/Chat.d.ts.map +1 -1
- package/dist/types/src/components/Chat/context.d.ts +5 -3
- package/dist/types/src/components/Chat/context.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatActions.d.ts +1 -1
- package/dist/types/src/components/ChatPrompt/ChatActions.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatMcpErrors.d.ts +3 -3
- package/dist/types/src/components/ChatPrompt/ChatMcpErrors.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatOptions.d.ts +3 -1
- package/dist/types/src/components/ChatPrompt/ChatOptions.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatOptions.stories.d.ts +4 -3
- package/dist/types/src/components/ChatPrompt/ChatOptions.stories.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatPrompt.d.ts +15 -5
- package/dist/types/src/components/ChatPrompt/ChatPrompt.d.ts.map +1 -1
- package/dist/types/src/components/ChatPrompt/ChatPrompt.stories.d.ts +1 -1
- package/dist/types/src/components/ChatPrompt/ChatStatus.d.ts +1 -1
- package/dist/types/src/components/ChatPrompt/ChatStatus.d.ts.map +1 -1
- package/dist/types/src/components/ConnectorAuthMenu/ConnectorAuthMenu.stories.d.ts +9 -0
- package/dist/types/src/components/ConnectorAuthMenu/ConnectorAuthMenu.stories.d.ts.map +1 -1
- package/dist/types/src/components/Toolbox/Toolbox.d.ts +3 -3
- package/dist/types/src/components/Toolbox/Toolbox.d.ts.map +1 -1
- package/dist/types/src/containers/ChatArticle/ChatArticle.d.ts.map +1 -1
- package/dist/types/src/containers/ChatArticle/ChatArticle.stories.d.ts +1 -1
- package/dist/types/src/containers/SpaceHomePrompt/SpaceHomePrompt.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +2 -2
- package/dist/types/src/hooks/index.d.ts.map +1 -1
- package/dist/types/src/hooks/{useChatProcessor.d.ts → useChatModel.d.ts} +6 -6
- package/dist/types/src/hooks/useChatModel.d.ts.map +1 -0
- package/dist/types/src/hooks/useDebug.d.ts +3 -3
- package/dist/types/src/hooks/useDebug.d.ts.map +1 -1
- package/dist/types/src/hooks/useHomeSuggestions.d.ts +3 -3
- package/dist/types/src/hooks/useHomeSuggestions.d.ts.map +1 -1
- package/dist/types/src/hooks/usePresets.d.ts +1 -1
- package/dist/types/src/hooks/usePresets.d.ts.map +1 -1
- package/dist/types/src/hooks/useSelectionContext.d.ts +3 -3
- package/dist/types/src/hooks/useSelectionContext.d.ts.map +1 -1
- package/dist/types/src/operations/analyze-turn.d.ts +5 -0
- package/dist/types/src/operations/analyze-turn.d.ts.map +1 -0
- package/dist/types/src/operations/analyze-turn.test.d.ts +2 -0
- package/dist/types/src/operations/analyze-turn.test.d.ts.map +1 -0
- package/dist/types/src/operations/create-chat.d.ts.map +1 -1
- package/dist/types/src/operations/generate-home-suggestions.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts.map +1 -1
- package/dist/types/src/operations/run-prompt-in-chat.d.ts.map +1 -1
- package/dist/types/src/review/turn-review.d.ts +58 -0
- package/dist/types/src/review/turn-review.d.ts.map +1 -0
- package/dist/types/src/review/turn-review.test.d.ts +2 -0
- package/dist/types/src/review/turn-review.test.d.ts.map +1 -0
- package/dist/types/src/skills/index.d.ts +1 -0
- package/dist/types/src/skills/index.d.ts.map +1 -1
- package/dist/types/src/skills/turn-review/TurnReviewSkill.d.ts +21 -0
- package/dist/types/src/skills/turn-review/TurnReviewSkill.d.ts.map +1 -0
- package/dist/types/src/skills/turn-review/index.d.ts +2 -0
- package/dist/types/src/skills/turn-review/index.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/Assistant.d.ts +1 -0
- package/dist/types/src/types/Assistant.d.ts.map +1 -1
- package/dist/types/src/types/AssistantCapabilities.d.ts +7 -1
- package/dist/types/src/types/AssistantCapabilities.d.ts.map +1 -1
- package/dist/types/src/types/Settings.d.ts +1 -0
- package/dist/types/src/types/Settings.d.ts.map +1 -1
- package/dist/types/src/util/agent-options.d.ts +27 -0
- package/dist/types/src/util/agent-options.d.ts.map +1 -0
- package/dist/types/src/util/agent-options.test.d.ts +2 -0
- package/dist/types/src/util/agent-options.test.d.ts.map +1 -0
- package/dist/types/src/util/default-skills.d.ts +10 -0
- package/dist/types/src/util/default-skills.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +89 -80
- package/src/capabilities/agent-service.ts +2 -1
- package/src/capabilities/skill-definition.ts +2 -1
- package/src/chat-model/chat-model.edge.test.ts +301 -0
- package/src/{processor/processor.node.test.ts → chat-model/chat-model.node.test.ts} +19 -9
- package/src/chat-model/chat-model.scripted.node.test.ts +441 -0
- package/src/{processor/processor.ts → chat-model/chat-model.ts} +140 -45
- package/src/{processor → chat-model}/index.ts +1 -1
- package/src/{processor → chat-model}/prompt.ts +2 -2
- package/src/{processor → chat-model}/streaming.node.test.ts +108 -116
- package/src/chat-model/testing/chat-model-recorder.ts +249 -0
- package/src/chat-model/testing/harness.ts +81 -0
- package/src/{processor → chat-model}/thread.test.ts +1 -1
- package/src/components/Chat/Chat.tsx +44 -38
- package/src/components/Chat/Error.stories.tsx +1 -1
- package/src/components/Chat/context.ts +5 -3
- package/src/components/ChatPrompt/ChatActions.tsx +1 -1
- package/src/components/ChatPrompt/ChatMcpErrors.tsx +6 -6
- package/src/components/ChatPrompt/ChatOptions.stories.tsx +37 -3
- package/src/components/ChatPrompt/ChatOptions.tsx +118 -10
- package/src/components/ChatPrompt/ChatPrompt.stories.tsx +10 -10
- package/src/components/ChatPrompt/ChatPrompt.tsx +44 -27
- package/src/components/ChatPrompt/ChatStatus.tsx +8 -7
- package/src/components/Toolbox/Toolbox.tsx +12 -13
- package/src/containers/AssistantSettings/AssistantSettings.tsx +1 -1
- package/src/containers/ChatArticle/ChatArticle.stories.tsx +1 -1
- package/src/containers/ChatArticle/ChatArticle.tsx +16 -6
- package/src/containers/ChatDialog/ChatDialog.tsx +4 -4
- package/src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx +110 -57
- package/src/hooks/index.ts +2 -2
- package/src/hooks/{useChatProcessor.ts → useChatModel.ts} +16 -14
- package/src/hooks/useDebug.ts +8 -8
- package/src/hooks/useHomeSuggestions.ts +14 -9
- package/src/hooks/usePresets.ts +2 -2
- package/src/hooks/useProcessEphemeralStatus.ts +1 -1
- package/src/hooks/useSelectionContext.ts +3 -5
- package/src/operations/analyze-turn.test.ts +194 -0
- package/src/operations/analyze-turn.ts +96 -0
- package/src/operations/create-chat.ts +4 -36
- package/src/operations/generate-home-suggestions.test.ts +1 -2
- package/src/operations/generate-home-suggestions.ts +17 -9
- package/src/operations/index.ts +2 -0
- package/src/operations/run-prompt-in-chat.ts +3 -2
- package/src/review/turn-review.test.ts +92 -0
- package/src/review/turn-review.ts +213 -0
- package/src/skills/index.ts +1 -0
- package/src/skills/turn-review/TurnReviewSkill.ts +46 -0
- package/src/skills/turn-review/index.ts +5 -0
- package/src/translations.ts +2 -0
- package/src/types/AssistantCapabilities.ts +8 -1
- package/src/types/Settings.ts +7 -0
- package/src/util/agent-options.test.ts +54 -0
- package/src/util/agent-options.ts +60 -0
- package/src/util/default-skills.ts +35 -0
- package/dist/lib/chunk-processor.mjs.map +0 -1
- package/dist/types/src/hooks/useChatProcessor.d.ts.map +0 -1
- package/dist/types/src/processor/index.d.ts.map +0 -1
- package/dist/types/src/processor/outbox.d.ts.map +0 -1
- package/dist/types/src/processor/outbox.test.d.ts.map +0 -1
- package/dist/types/src/processor/presets.d.ts.map +0 -1
- package/dist/types/src/processor/presets.test.d.ts.map +0 -1
- package/dist/types/src/processor/processor.d.ts.map +0 -1
- package/dist/types/src/processor/processor.node.test.d.ts +0 -2
- package/dist/types/src/processor/processor.node.test.d.ts.map +0 -1
- package/dist/types/src/processor/prompt.d.ts.map +0 -1
- package/dist/types/src/processor/prompt.test.d.ts.map +0 -1
- package/dist/types/src/processor/streaming.node.test.d.ts.map +0 -1
- package/dist/types/src/processor/thread.d.ts.map +0 -1
- package/dist/types/src/processor/thread.test.d.ts.map +0 -1
- /package/dist/types/src/{processor → chat-model}/outbox.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/outbox.test.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/presets.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/presets.test.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/prompt.test.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/streaming.node.test.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/thread.d.ts +0 -0
- /package/dist/types/src/{processor → chat-model}/thread.test.d.ts +0 -0
- /package/src/{processor → chat-model}/outbox.test.ts +0 -0
- /package/src/{processor → chat-model}/outbox.ts +0 -0
- /package/src/{processor → chat-model}/presets.test.ts +0 -0
- /package/src/{processor → chat-model}/presets.ts +0 -0
- /package/src/{processor → chat-model}/prompt.test.ts +0 -0
- /package/src/{processor → chat-model}/thread.ts +0 -0
|
@@ -2,11 +2,14 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { useAtomValue } from '@effect/atom-react/Hooks';
|
|
6
|
+
import * as Atom from 'effect/reactivity/Atom';
|
|
5
7
|
import React, { type JSX, useCallback, useMemo, useState } from 'react';
|
|
6
8
|
|
|
7
|
-
import { Provider } from '@dxos/ai';
|
|
9
|
+
import { Provider, SessionConfig } from '@dxos/ai';
|
|
8
10
|
import * as Hooks from '@dxos/app-framework/Hooks';
|
|
9
11
|
import { type AiContext } from '@dxos/assistant';
|
|
12
|
+
import * as Agent from '@dxos/assistant/Agent';
|
|
10
13
|
import type * as Chat from '@dxos/assistant/Chat';
|
|
11
14
|
import * as McpServer from '@dxos/compute/McpServer';
|
|
12
15
|
import { type Database, Filter, Obj, Ref, type Registry, Type, URI } from '@dxos/echo';
|
|
@@ -19,6 +22,7 @@ import { SearchList, useSearchListResults } from '@dxos/react-ui-search';
|
|
|
19
22
|
import * as Button from '@dxos/react-ui/Button';
|
|
20
23
|
import * as Field from '@dxos/react-ui/Field';
|
|
21
24
|
import * as UiHooks from '@dxos/react-ui/Hooks';
|
|
25
|
+
import * as Icon from '@dxos/react-ui/Icon';
|
|
22
26
|
import * as Input from '@dxos/react-ui/Input';
|
|
23
27
|
import * as Layout from '@dxos/react-ui/Layout';
|
|
24
28
|
import * as Popover from '@dxos/react-ui/Popover';
|
|
@@ -41,7 +45,8 @@ import {
|
|
|
41
45
|
import { meta } from '#meta';
|
|
42
46
|
import { Assistant, AssistantCapabilities, AssistantPreset } from '#types';
|
|
43
47
|
|
|
44
|
-
import { resolveProvider } from '../../
|
|
48
|
+
import { resolveProvider } from '../../chat-model/index.ts';
|
|
49
|
+
import { agentOptions } from '../../util/agent-options.ts';
|
|
45
50
|
|
|
46
51
|
const styles = {
|
|
47
52
|
panel: 'w-[calc(100dvw-.5rem)] sm:w-max max-w-document-width',
|
|
@@ -52,12 +57,23 @@ export type ChatOptionsProps = AssistantPreset.ChatPresetProps & {
|
|
|
52
57
|
chat?: Chat.Chat;
|
|
53
58
|
context?: AiContext.Binder;
|
|
54
59
|
registry?: Registry.Registry;
|
|
60
|
+
/** Whether the conversation has begun; its agent is fixed from then on. */
|
|
61
|
+
started?: boolean;
|
|
55
62
|
};
|
|
56
63
|
|
|
57
64
|
/**
|
|
58
65
|
* Manages the runtime context for the chat.
|
|
59
66
|
*/
|
|
60
|
-
export const ChatOptions = ({
|
|
67
|
+
export const ChatOptions = ({
|
|
68
|
+
db,
|
|
69
|
+
chat,
|
|
70
|
+
context,
|
|
71
|
+
registry,
|
|
72
|
+
started,
|
|
73
|
+
presets,
|
|
74
|
+
preset,
|
|
75
|
+
onPresetChange,
|
|
76
|
+
}: ChatOptionsProps) => {
|
|
61
77
|
const { t } = UiHooks.useTranslation(meta.profile.key);
|
|
62
78
|
|
|
63
79
|
return (
|
|
@@ -91,7 +107,15 @@ export const ChatOptions = ({ db, chat, context, registry, presets, preset, onPr
|
|
|
91
107
|
</Popover.Trigger>
|
|
92
108
|
<Popover.Content classNames={styles.panel}>
|
|
93
109
|
{/* No Body: each tab's list and the tab bar share the popover's own padding. */}
|
|
94
|
-
|
|
110
|
+
{/* A chat that has not begun opens on its agent, since that is the choice it is about to lose. */}
|
|
111
|
+
<Tabs.Root
|
|
112
|
+
orientation='horizontal'
|
|
113
|
+
defaultValue={started ? 'view' : 'agent'}
|
|
114
|
+
classNames='grid grid-rows-[1fr_40px] w-full'
|
|
115
|
+
>
|
|
116
|
+
<Tabs.Content tabIndex={-1} classNames='dx-focus-ring-inset overflow-hidden' value='agent'>
|
|
117
|
+
<AgentPanel chat={chat} started={started} />
|
|
118
|
+
</Tabs.Content>
|
|
95
119
|
<Tabs.Content tabIndex={-1} classNames='dx-focus-ring-inset overflow-hidden' value='view'>
|
|
96
120
|
<ViewPanel chat={chat} />
|
|
97
121
|
</Tabs.Content>
|
|
@@ -108,6 +132,12 @@ export const ChatOptions = ({ db, chat, context, registry, presets, preset, onPr
|
|
|
108
132
|
<EnvironmentPanel chat={chat} />
|
|
109
133
|
</Tabs.Content>
|
|
110
134
|
<Tabs.List classNames='p-1 gap-1 border-t border-separator'>
|
|
135
|
+
<Tabs.Trigger
|
|
136
|
+
value='agent'
|
|
137
|
+
icon='ph--robot--regular'
|
|
138
|
+
label={t('options.agent.title')}
|
|
139
|
+
data-testid='assistant.options.agent'
|
|
140
|
+
/>
|
|
111
141
|
<Tabs.Trigger value='view' icon='ph--eye--regular' label={t('chat-view.title')} />
|
|
112
142
|
<Tabs.Trigger value='skills' icon='ph--student--regular' label={t('options.skills.title')} />
|
|
113
143
|
<Tabs.Trigger value='mcp-servers' icon='ph--plugs-connected--regular' label={t('options.mcp.title')} />
|
|
@@ -126,6 +156,77 @@ export const ChatOptions = ({ db, chat, context, registry, presets, preset, onPr
|
|
|
126
156
|
</Tabs.Root>
|
|
127
157
|
</Popover.Content>
|
|
128
158
|
</Popover.Root>
|
|
159
|
+
|
|
160
|
+
<AgentBadge chat={chat} />
|
|
161
|
+
</Layout.Flex>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/** The chat's agent and the picker's rows, live with each agent's availability. */
|
|
166
|
+
const useChatAgent = (chat: Chat.Chat | undefined) => {
|
|
167
|
+
const agents = Hooks.useOptionalCapabilities(AssistantCapabilities.Agent);
|
|
168
|
+
const availabilityAtom = useMemo(() => Atom.make((get) => agents.map((agent) => get(agent.availability))), [agents]);
|
|
169
|
+
const availability = useAtomValue(availabilityAtom);
|
|
170
|
+
const [session] = useObject(chat, 'session');
|
|
171
|
+
const current = SessionConfig.harnessOf(session);
|
|
172
|
+
const options = useMemo(() => agentOptions({ agents, availability, current }), [agents, availability, current]);
|
|
173
|
+
return { current, options };
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Which agent runs this chat. Picked before the first message only: an agent keeps its own session
|
|
178
|
+
* with the model, so one switched in mid-conversation would answer without the turns before it.
|
|
179
|
+
*/
|
|
180
|
+
const AgentPanel = ({ chat, started }: Pick<ChatOptionsProps, 'chat' | 'started'>) => {
|
|
181
|
+
const { t } = UiHooks.useTranslation(meta.profile.key);
|
|
182
|
+
const { current, options } = useChatAgent(chat);
|
|
183
|
+
const handleChange = useCallback(
|
|
184
|
+
(harness: string) => {
|
|
185
|
+
if (chat) {
|
|
186
|
+
Obj.update(chat, (chat) => {
|
|
187
|
+
chat.session = { ...chat.session, harness };
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
[chat],
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<Layout.Flex column classNames='dx-expand' data-testid='assistant.agents'>
|
|
196
|
+
<Listbox.Root value={current} onValueChange={handleChange} disabled={!chat || started} autoFocus items={options}>
|
|
197
|
+
<Listbox.Content gutter='none' aria-label={t('options.agent.title')}>
|
|
198
|
+
{options.map(({ value }) => (
|
|
199
|
+
<Listbox.Item
|
|
200
|
+
key={value}
|
|
201
|
+
id={value}
|
|
202
|
+
classNames='dx-focus-ring rounded-xs'
|
|
203
|
+
data-testid={`assistant.agents.${value}`}
|
|
204
|
+
/>
|
|
205
|
+
))}
|
|
206
|
+
</Listbox.Content>
|
|
207
|
+
</Listbox.Root>
|
|
208
|
+
{started && <p className='px-form-chrome py-1 text-xs text-fg-muted'>{t('agent-locked.message')}</p>}
|
|
209
|
+
</Layout.Flex>
|
|
210
|
+
);
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
/** Names the agent beside the options when it is not Composer's own, so a reader knows who answers. */
|
|
214
|
+
const AgentBadge = ({ chat }: Pick<ChatOptionsProps, 'chat'>) => {
|
|
215
|
+
const { current, options } = useChatAgent(chat);
|
|
216
|
+
const agent = options.find(({ value }) => value === current);
|
|
217
|
+
if (!agent || agent.value === SessionConfig.COMPOSER_HARNESS) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return (
|
|
222
|
+
<Layout.Flex
|
|
223
|
+
align='center'
|
|
224
|
+
gap='xs'
|
|
225
|
+
classNames='text-xs text-fg-muted whitespace-nowrap'
|
|
226
|
+
data-testid='assistant.agent'
|
|
227
|
+
>
|
|
228
|
+
<Icon.Icon icon={agent.icon} size='sm' />
|
|
229
|
+
<span>{agent.label}</span>
|
|
129
230
|
</Layout.Flex>
|
|
130
231
|
);
|
|
131
232
|
};
|
|
@@ -152,7 +253,7 @@ const SkillsPanel = ({ registry, db, context }: Pick<ChatOptionsProps, 'registry
|
|
|
152
253
|
const isActive = activeSkills.has(skillId);
|
|
153
254
|
return (
|
|
154
255
|
<SearchList.Item
|
|
155
|
-
classNames='
|
|
256
|
+
classNames='overflow-hidden'
|
|
156
257
|
key={skillId}
|
|
157
258
|
value={skillId}
|
|
158
259
|
label={skill.name}
|
|
@@ -211,10 +312,17 @@ const EnvironmentPanel = ({ chat }: Pick<ChatOptionsProps, 'chat'>) => {
|
|
|
211
312
|
// Offered only where an edge service is configured, which is the same condition that decides
|
|
212
313
|
// whether `RemoteProcessManager` is the real manager or `layerNoop`: against the noop a spawn has
|
|
213
314
|
// no `list` or `spawn`, so choosing `remote` would persist a flag the next prompt cannot honour.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const
|
|
315
|
+
// An agent's chat runs on EDGE (`Agent.chatLocation`) unless explicitly kept local: with EDGE configured local is
|
|
316
|
+
// no choice there, and without it local stays offered so the chat can be moved somewhere it can run.
|
|
317
|
+
const edgeConfigured = Boolean(client?.config.values.runtime?.services?.edge?.url);
|
|
318
|
+
const agentOnEdge = chat !== undefined && Agent.isAgentChat(chat) && chat.remote !== false;
|
|
319
|
+
const environments =
|
|
320
|
+
agentOnEdge && edgeConfigured
|
|
321
|
+
? CHAT_ENVIRONMENTS.filter((environment) => environment === 'remote')
|
|
322
|
+
: agentOnEdge || edgeConfigured
|
|
323
|
+
? CHAT_ENVIRONMENTS
|
|
324
|
+
: CHAT_ENVIRONMENTS.filter((environment) => environment !== 'remote');
|
|
325
|
+
const value: ChatEnvironment = remote || agentOnEdge ? 'remote' : 'local';
|
|
218
326
|
const handleChange = useCallback((value: string) => setRemote(value === 'remote'), [setRemote]);
|
|
219
327
|
|
|
220
328
|
return (
|
|
@@ -608,7 +716,7 @@ export const ObjectsPanel = ({ db, context }: Pick<ChatOptionsProps, 'db' | 'con
|
|
|
608
716
|
const styles = hue ? getStyles(hue) : undefined;
|
|
609
717
|
return (
|
|
610
718
|
<SearchList.Item
|
|
611
|
-
classNames='
|
|
719
|
+
classNames='overflow-hidden'
|
|
612
720
|
key={object.id}
|
|
613
721
|
value={object.id}
|
|
614
722
|
icon={icon}
|
|
@@ -24,7 +24,7 @@ import { useRegistry, useSpaces } from '@dxos/react-client/echo';
|
|
|
24
24
|
import { Loading, withLayout, withTheme } from '@dxos/react-ui/testing';
|
|
25
25
|
import { Message, Task } from '@dxos/types';
|
|
26
26
|
|
|
27
|
-
import {
|
|
27
|
+
import { useChatModel, useChatServices, usePresets } from '#hooks';
|
|
28
28
|
import { AssistantPlugin } from '#plugin';
|
|
29
29
|
import { translations } from '#translations';
|
|
30
30
|
import { AssistantCapabilities } from '#types';
|
|
@@ -38,7 +38,7 @@ type StoryArgs = {
|
|
|
38
38
|
/** Prompts already queued on the feed, as a submit-while-busy leaves them. */
|
|
39
39
|
queued?: string[];
|
|
40
40
|
/**
|
|
41
|
-
* Holds the
|
|
41
|
+
* Holds the chat model `active` while leaving `streaming` false — a turn parked in a tool call.
|
|
42
42
|
* This is the state the send/stop control has to get right, and no scripted model can hold it
|
|
43
43
|
* still long enough to assert against.
|
|
44
44
|
*/
|
|
@@ -55,26 +55,26 @@ const DefaultStory = ({ tasksVisible: initialTasksVisible, running }: StoryArgs)
|
|
|
55
55
|
const { preset, ...chatProps } = usePresets(settings, chat);
|
|
56
56
|
const db = space?.db;
|
|
57
57
|
const runtime = useChatServices({ id: db?.spaceId });
|
|
58
|
-
const
|
|
58
|
+
const chatModel = useChatModel({ db, chat, preset, runtime, registry, settings });
|
|
59
59
|
|
|
60
60
|
// Drives the control's mode from the outside, so the assertion does not race a real turn. The
|
|
61
|
-
//
|
|
62
|
-
// The same registry `
|
|
61
|
+
// chat model reads its atoms from the ambient registry, which is what the story writes to.
|
|
62
|
+
// The same registry `useChatModel` hands the chat model, so a write here is a write it sees.
|
|
63
63
|
const atomRegistry = useContext(RegistryContext);
|
|
64
64
|
useEffect(() => {
|
|
65
|
-
if (running &&
|
|
66
|
-
atomRegistry.set(
|
|
65
|
+
if (running && chatModel) {
|
|
66
|
+
atomRegistry.set(chatModel.active, true);
|
|
67
67
|
}
|
|
68
|
-
}, [running,
|
|
68
|
+
}, [running, chatModel, atomRegistry]);
|
|
69
69
|
|
|
70
|
-
if (!chat || !db || !
|
|
70
|
+
if (!chat || !db || !chatModel) {
|
|
71
71
|
return <Loading />;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
return (
|
|
75
75
|
<div className='flex justify-center p-4'>
|
|
76
76
|
<div className='w-full max-w-document-width'>
|
|
77
|
-
<Chat.Root chat={chat} db={db}
|
|
77
|
+
<Chat.Root chat={chat} db={db} chatModel={chatModel}>
|
|
78
78
|
<Chat.Status classNames='px-3 rounded-sm bg-group-surface' />
|
|
79
79
|
{/* `attendableId` is the graph node contributed actions are filed under; the story's chat
|
|
80
80
|
has no node, so the row shows only its own controls unless a plugin renders one. The
|
|
@@ -8,10 +8,11 @@ import * as Option from 'effect/Option';
|
|
|
8
8
|
import type * as Atom from 'effect/reactivity/Atom';
|
|
9
9
|
import React, { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
|
|
10
10
|
|
|
11
|
+
import { type AiContext } from '@dxos/assistant';
|
|
11
12
|
import type * as Chat from '@dxos/assistant/Chat';
|
|
12
13
|
import { type Event } from '@dxos/async';
|
|
13
14
|
import * as Project from '@dxos/compute/Project';
|
|
14
|
-
import { type Database, Obj } from '@dxos/echo';
|
|
15
|
+
import { type Database, Obj, type Registry } from '@dxos/echo';
|
|
15
16
|
import { useObject } from '@dxos/echo-react';
|
|
16
17
|
import {
|
|
17
18
|
ChatEditor,
|
|
@@ -22,17 +23,17 @@ import {
|
|
|
22
23
|
} from '@dxos/react-ui-chat';
|
|
23
24
|
import type { ActionGraphProps } from '@dxos/react-ui-menu';
|
|
24
25
|
import * as Hooks from '@dxos/react-ui/Hooks';
|
|
26
|
+
import * as Layout from '@dxos/react-ui/Layout';
|
|
25
27
|
import type * as Util from '@dxos/react-ui/Util';
|
|
26
28
|
import { pendingText } from '@dxos/ui-editor';
|
|
27
|
-
import { mx } from '@dxos/ui-theme';
|
|
28
29
|
import { type Merge } from '@dxos/util';
|
|
29
30
|
|
|
30
31
|
import { useChatKeymapExtensions } from '#hooks';
|
|
31
32
|
import { meta } from '#meta';
|
|
32
33
|
import { AssistantPreset } from '#types';
|
|
33
34
|
|
|
35
|
+
import { type ChatModel, getChatModelState } from '../../chat-model/index.ts';
|
|
34
36
|
import { TaskSlashCommands } from '../../commands/index.ts';
|
|
35
|
-
import { type AiChatProcessor, getProcessorState } from '../../processor/index.ts';
|
|
36
37
|
import { type ChatEvent } from '../Chat/index.ts';
|
|
37
38
|
import { ChatActions, type ChatActionsProps } from './ChatActions.tsx';
|
|
38
39
|
import { ChatMcpErrors } from './ChatMcpErrors.tsx';
|
|
@@ -50,8 +51,15 @@ export type ChatPromptProps = Merge<
|
|
|
50
51
|
expandable?: boolean;
|
|
51
52
|
db?: Database.Database;
|
|
52
53
|
chat?: Chat.Chat;
|
|
53
|
-
/** Undefined while the
|
|
54
|
-
|
|
54
|
+
/** Undefined while the chat model is still opening: the prompt takes text but holds it until then. */
|
|
55
|
+
chatModel?: ChatModel;
|
|
56
|
+
/**
|
|
57
|
+
* Bindings for a draft with no chat model, whose caller handles the submit event: backs the context
|
|
58
|
+
* controls and lets the prompt send.
|
|
59
|
+
*/
|
|
60
|
+
context?: AiContext.Binder;
|
|
61
|
+
/** Skill registry for the context controls when there is no chat model. */
|
|
62
|
+
registry?: Registry.Registry;
|
|
55
63
|
event: Event<ChatEvent>;
|
|
56
64
|
/** Whether the checklist beside the prompt is shown; the toggle renders only when provided. */
|
|
57
65
|
tasksVisible?: boolean;
|
|
@@ -71,6 +79,8 @@ export type ChatPromptProps = Merge<
|
|
|
71
79
|
queueSize?: number;
|
|
72
80
|
/** The most prompts that may wait behind a running turn; past it the prompt takes no more until one is taken up. */
|
|
73
81
|
maxQueue?: number;
|
|
82
|
+
/** Whether the conversation has begun, which fixes its agent. */
|
|
83
|
+
started?: boolean;
|
|
74
84
|
/** Object the chat is attached to; its project instructions (if any) supply sentinel-command completion. */
|
|
75
85
|
companionTo?: Obj.Unknown;
|
|
76
86
|
}>,
|
|
@@ -82,7 +92,9 @@ export const ChatPrompt = ({
|
|
|
82
92
|
outline,
|
|
83
93
|
db,
|
|
84
94
|
chat,
|
|
85
|
-
|
|
95
|
+
chatModel,
|
|
96
|
+
context: draftContext,
|
|
97
|
+
registry: draftRegistry,
|
|
86
98
|
event,
|
|
87
99
|
tasksVisible,
|
|
88
100
|
attendableId,
|
|
@@ -96,13 +108,16 @@ export const ChatPrompt = ({
|
|
|
96
108
|
presets,
|
|
97
109
|
preset,
|
|
98
110
|
companionTo,
|
|
111
|
+
started,
|
|
99
112
|
onPresetChange,
|
|
100
113
|
}: ChatPromptProps) => {
|
|
101
114
|
const { t } = Hooks.useTranslation(meta.profile.key);
|
|
102
|
-
const
|
|
103
|
-
const error = useAtomValue(
|
|
104
|
-
const streaming = useAtomValue(
|
|
105
|
-
const active = useAtomValue(
|
|
115
|
+
const chatModelState = getChatModelState(chatModel);
|
|
116
|
+
const error = useAtomValue(chatModelState.error).pipe(Option.getOrUndefined);
|
|
117
|
+
const streaming = useAtomValue(chatModelState.streaming);
|
|
118
|
+
const active = useAtomValue(chatModelState.active);
|
|
119
|
+
const context = chatModel?.context ?? draftContext;
|
|
120
|
+
const registry = chatModel?.registry ?? draftRegistry;
|
|
106
121
|
|
|
107
122
|
const editorRef = useRef<ChatEditorController>(null);
|
|
108
123
|
useEffect(() => {
|
|
@@ -157,7 +172,7 @@ export const ChatPrompt = ({
|
|
|
157
172
|
|
|
158
173
|
// A full queue stops taking prompts: what is typed stays in the editor until the agent takes one up.
|
|
159
174
|
const queueFull = active && queueSize >= maxQueue;
|
|
160
|
-
const canSend = hasText &&
|
|
175
|
+
const canSend = hasText && context != null && !queueFull;
|
|
161
176
|
|
|
162
177
|
const extensions = useMemo(
|
|
163
178
|
() => [keymapExtensions, pendingText(), commandsExtension, emptinessExtension],
|
|
@@ -165,17 +180,17 @@ export const ChatPrompt = ({
|
|
|
165
180
|
);
|
|
166
181
|
|
|
167
182
|
// Submits while a turn is running too: the agent's input queue is feed state, so the prompt is
|
|
168
|
-
// queued behind the running turn rather than dropped (`Chat.Root` sends it through the
|
|
183
|
+
// queued behind the running turn rather than dropped (`Chat.Root` sends it through the chat model's
|
|
169
184
|
// outbox, which queues it while the agent is busy).
|
|
170
185
|
const handleSubmit = useCallback<NonNullable<ChatEditorProps['onSubmit']>>(
|
|
171
186
|
(text) => {
|
|
172
|
-
if (!
|
|
187
|
+
if (!context || queueFull) {
|
|
173
188
|
return false;
|
|
174
189
|
}
|
|
175
190
|
event.emit({ type: 'submit', text });
|
|
176
191
|
return true;
|
|
177
192
|
},
|
|
178
|
-
[event,
|
|
193
|
+
[event, context, queueFull],
|
|
179
194
|
);
|
|
180
195
|
|
|
181
196
|
// Routed through `handleSubmit` so the button and the Enter keybinding share one submit path;
|
|
@@ -198,19 +213,20 @@ export const ChatPrompt = ({
|
|
|
198
213
|
);
|
|
199
214
|
|
|
200
215
|
return (
|
|
201
|
-
<
|
|
216
|
+
<Layout.Flex
|
|
217
|
+
column
|
|
202
218
|
data-testid='assistant.prompt'
|
|
203
219
|
role='group'
|
|
204
|
-
|
|
205
|
-
'
|
|
220
|
+
classNames={[
|
|
221
|
+
'w-full dx-density-md',
|
|
206
222
|
outline &&
|
|
207
223
|
'dx-group-surface rounded-sm border border-separator-subtle transition transition-border [&:has(.cm-content:focus)]:border-separator',
|
|
208
224
|
classNames,
|
|
209
|
-
|
|
225
|
+
]}
|
|
210
226
|
>
|
|
211
|
-
{
|
|
227
|
+
{chatModel && <ChatMcpErrors chatModel={chatModel} />}
|
|
212
228
|
|
|
213
|
-
<
|
|
229
|
+
<Layout.Flex gap='sm' classNames='p-2'>
|
|
214
230
|
<ChatStatusIndicator classNames='p-1' preset={preset} error={error} processing={streaming} />
|
|
215
231
|
<ChatEditor
|
|
216
232
|
ref={editorRef}
|
|
@@ -222,7 +238,7 @@ export const ChatPrompt = ({
|
|
|
222
238
|
extensions={extensions}
|
|
223
239
|
onSubmit={handleSubmit}
|
|
224
240
|
/>
|
|
225
|
-
</
|
|
241
|
+
</Layout.Flex>
|
|
226
242
|
|
|
227
243
|
{db &&
|
|
228
244
|
settings && (
|
|
@@ -243,20 +259,21 @@ export const ChatPrompt = ({
|
|
|
243
259
|
<ChatOptions
|
|
244
260
|
db={db}
|
|
245
261
|
chat={chat}
|
|
246
|
-
registry={
|
|
247
|
-
context={
|
|
262
|
+
registry={registry}
|
|
263
|
+
context={context}
|
|
264
|
+
started={started}
|
|
248
265
|
preset={preset}
|
|
249
266
|
presets={presets}
|
|
250
267
|
onPresetChange={onPresetChange}
|
|
251
268
|
/>
|
|
252
|
-
<
|
|
253
|
-
{
|
|
254
|
-
</
|
|
269
|
+
<Layout.Flex classNames='h-6 grow overflow-x-auto scrollbar-none'>
|
|
270
|
+
{context && <ChatReferences db={db} context={context} />}
|
|
271
|
+
</Layout.Flex>
|
|
255
272
|
</>
|
|
256
273
|
}
|
|
257
274
|
/>
|
|
258
275
|
)}
|
|
259
|
-
</
|
|
276
|
+
</Layout.Flex>
|
|
260
277
|
);
|
|
261
278
|
};
|
|
262
279
|
|
|
@@ -8,6 +8,7 @@ import { Alarm } from '@dxos/assistant';
|
|
|
8
8
|
import { ChatStatus as NaturalChatStatus, formatElapsed } from '@dxos/react-ui-chat';
|
|
9
9
|
import { Matrix } from '@dxos/react-ui-experimental';
|
|
10
10
|
import * as Icon from '@dxos/react-ui/Icon';
|
|
11
|
+
import * as Layout from '@dxos/react-ui/Layout';
|
|
11
12
|
import type * as Util from '@dxos/react-ui/Util';
|
|
12
13
|
import { type ContentBlock } from '@dxos/types';
|
|
13
14
|
import { Unit } from '@dxos/util';
|
|
@@ -52,8 +53,8 @@ export type ChatStatusViewProps = ChatStreamStatusProps & {
|
|
|
52
53
|
* - cumulative session total tokens across all `stats` content blocks
|
|
53
54
|
*/
|
|
54
55
|
export const ChatStatus = ({ classNames, icon }: ChatStreamStatusProps) => {
|
|
55
|
-
// Read `messages` from the chat context (combines `useQuery(queue)` + the
|
|
56
|
-
// pending atom) rather than `
|
|
56
|
+
// Read `messages` from the chat context (combines `useQuery(queue)` + the chat model's
|
|
57
|
+
// pending atom) rather than `chatModel.messages` directly — the latter only contains
|
|
57
58
|
// blocks streamed via the ephemeral `PartialBlock` channel, while finalized blocks
|
|
58
59
|
// (including the per-turn `stats` block we read for token counts) are submitted to the
|
|
59
60
|
// feed via `_submitMessage` and only show up through `useQuery`.
|
|
@@ -92,7 +93,7 @@ export const ChatStatus = ({ classNames, icon }: ChatStreamStatusProps) => {
|
|
|
92
93
|
|
|
93
94
|
/**
|
|
94
95
|
* The pill itself, given resolved values. Split from {@link ChatStatus} so each slot — elapsed,
|
|
95
|
-
* tokens, the next alarm — can be mounted and asserted in a story without a live
|
|
96
|
+
* tokens, the next alarm — can be mounted and asserted in a story without a live chat model.
|
|
96
97
|
*/
|
|
97
98
|
export const ChatStatusView = ({
|
|
98
99
|
classNames,
|
|
@@ -115,7 +116,7 @@ export const ChatStatusView = ({
|
|
|
115
116
|
<NaturalChatStatus.Icon>
|
|
116
117
|
<Matrix
|
|
117
118
|
classNames='size-5'
|
|
118
|
-
dotClassNames='bg-primary-
|
|
119
|
+
dotClassNames='bg-primary-bg'
|
|
119
120
|
dim={4}
|
|
120
121
|
dotSize={3}
|
|
121
122
|
count={10}
|
|
@@ -125,9 +126,9 @@ export const ChatStatusView = ({
|
|
|
125
126
|
</NaturalChatStatus.Icon>
|
|
126
127
|
)}
|
|
127
128
|
{show && (
|
|
128
|
-
<
|
|
129
|
+
<Layout.Flex align='center'>
|
|
129
130
|
{requestTiming && (
|
|
130
|
-
<NaturalChatStatus.Text classNames={isRunning && 'text-sky-
|
|
131
|
+
<NaturalChatStatus.Text classNames={isRunning && 'text-sky-text'}>
|
|
131
132
|
<Elapsed timing={requestTiming} />
|
|
132
133
|
</NaturalChatStatus.Text>
|
|
133
134
|
)}
|
|
@@ -163,7 +164,7 @@ export const ChatStatusView = ({
|
|
|
163
164
|
</NaturalChatStatus.Text>
|
|
164
165
|
</>
|
|
165
166
|
)}
|
|
166
|
-
</
|
|
167
|
+
</Layout.Flex>
|
|
167
168
|
)}
|
|
168
169
|
</NaturalChatStatus.Root>
|
|
169
170
|
);
|
|
@@ -9,13 +9,13 @@ import type * as Skill from '@dxos/compute/Skill';
|
|
|
9
9
|
import { type Database, Filter, type Ref } from '@dxos/echo';
|
|
10
10
|
import { useQuery } from '@dxos/echo-react';
|
|
11
11
|
import { log } from '@dxos/log';
|
|
12
|
+
import * as Layout from '@dxos/react-ui/Layout';
|
|
12
13
|
import * as ScrollArea from '@dxos/react-ui/ScrollArea';
|
|
13
14
|
import * as Util from '@dxos/react-ui/Util';
|
|
14
|
-
import { mx } from '@dxos/ui-theme';
|
|
15
15
|
|
|
16
16
|
import { AssistantService } from '#types';
|
|
17
17
|
|
|
18
|
-
import { type
|
|
18
|
+
import { type ChatModel } from '../../chat-model/index.ts';
|
|
19
19
|
|
|
20
20
|
export type ToolboxProps = {
|
|
21
21
|
services?: { service: AssistantService.ServiceType }[];
|
|
@@ -82,40 +82,39 @@ type SectionProps = {
|
|
|
82
82
|
|
|
83
83
|
const Section = ({ title, items, striped }: SectionProps) => {
|
|
84
84
|
const stripeClassNames = 'odd:bg-neutral-50 dark:odd:bg-neutral-800';
|
|
85
|
-
const
|
|
86
|
-
const subGridClassNames = mx('col-span-full grid grid-cols-subgrid text-xs px-2', striped && stripeClassNames);
|
|
85
|
+
const subGridClassNames = ['text-xs px-2', striped && stripeClassNames];
|
|
87
86
|
|
|
88
87
|
return (
|
|
89
88
|
<div>
|
|
90
89
|
<h1 className='px-2 text-sm'>{title}</h1>
|
|
91
|
-
<
|
|
90
|
+
<Layout.Grid cols={['8rem', 'fill']}>
|
|
92
91
|
{items.map(({ name, description, subitems }, i) => (
|
|
93
92
|
<Fragment key={i}>
|
|
94
93
|
{name && (
|
|
95
|
-
<
|
|
94
|
+
<Layout.Grid cols='subgrid' classNames={subGridClassNames}>
|
|
96
95
|
<div className='truncate text-accent-text'>{name}</div>
|
|
97
96
|
<div className='line-clamp-2'>{description}</div>
|
|
98
|
-
</
|
|
97
|
+
</Layout.Grid>
|
|
99
98
|
)}
|
|
100
99
|
{subitems?.map(({ name, description }, i) => (
|
|
101
|
-
<
|
|
100
|
+
<Layout.Grid key={i} cols='subgrid' classNames={subGridClassNames}>
|
|
102
101
|
<div className='truncate'>{name}</div>
|
|
103
102
|
<div className='line-clamp-3 text-fg-subtle'>{description}</div>
|
|
104
|
-
</
|
|
103
|
+
</Layout.Grid>
|
|
105
104
|
))}
|
|
106
105
|
</Fragment>
|
|
107
106
|
))}
|
|
108
|
-
</
|
|
107
|
+
</Layout.Grid>
|
|
109
108
|
</div>
|
|
110
109
|
);
|
|
111
110
|
};
|
|
112
111
|
|
|
113
112
|
export type ToolboxPanelProps = Util.ThemedClassName<{
|
|
114
113
|
db?: Database.Database;
|
|
115
|
-
|
|
114
|
+
chatModel?: ChatModel;
|
|
116
115
|
}>;
|
|
117
116
|
|
|
118
|
-
export const ToolboxPanel = ({ classNames, db,
|
|
117
|
+
export const ToolboxPanel = ({ classNames, db, chatModel }: ToolboxPanelProps) => {
|
|
119
118
|
// Registered services.
|
|
120
119
|
const services = useQuery(db, Filter.type(AssistantService.ServiceType));
|
|
121
120
|
const [serviceTools, setServiceTools] = useState<{ service: AssistantService.ServiceType }[]>([]);
|
|
@@ -136,7 +135,7 @@ export const ToolboxPanel = ({ classNames, db, processor }: ToolboxPanelProps) =
|
|
|
136
135
|
return (
|
|
137
136
|
<Toolbox
|
|
138
137
|
classNames={classNames}
|
|
139
|
-
skills={
|
|
138
|
+
skills={chatModel?.context.getSkills()}
|
|
140
139
|
services={serviceTools}
|
|
141
140
|
functions={functions}
|
|
142
141
|
/>
|
|
@@ -15,7 +15,7 @@ import * as UiHooks from '@dxos/react-ui/Hooks';
|
|
|
15
15
|
import { meta } from '#meta';
|
|
16
16
|
import { Assistant, AssistantCapabilities, Ollama } from '#types';
|
|
17
17
|
|
|
18
|
-
import { presetsForProvider, resolveProvider } from '../../
|
|
18
|
+
import { presetsForProvider, resolveProvider } from '../../chat-model/index.ts';
|
|
19
19
|
import { OllamaModels } from './OllamaModels.tsx';
|
|
20
20
|
|
|
21
21
|
export type AssistantSettingsProps = AppSurface.SettingsProps<Assistant.Settings>;
|
|
@@ -336,7 +336,7 @@ export const Send: Story = {
|
|
|
336
336
|
/**
|
|
337
337
|
* Submitting again without waiting for the running turn: the second prompt is QUEUED on the feed and
|
|
338
338
|
* runs after the first, rather than being dropped (the composer used to ignore a submit while a turn
|
|
339
|
-
* was active) or cancelling the turn in flight (`
|
|
339
|
+
* was active) or cancelling the turn in flight (`chatModel.request` interrupts, `enqueue` does not).
|
|
340
340
|
*
|
|
341
341
|
* Deliberately no wait between the two submits — that is the whole case. Both replies landing is what
|
|
342
342
|
* proves the second prompt survived; the agent-level ordering and mid-turn arrival are pinned
|
|
@@ -12,6 +12,7 @@ import * as ToolkitHooks from '@dxos/app-toolkit/Hooks';
|
|
|
12
12
|
import type * as Chat from '@dxos/assistant/Chat';
|
|
13
13
|
import { Obj } from '@dxos/echo';
|
|
14
14
|
import { useObject } from '@dxos/echo-react';
|
|
15
|
+
import { useIdentity } from '@dxos/halo-react';
|
|
15
16
|
import * as ClientOperation from '@dxos/plugin-client/ClientOperation';
|
|
16
17
|
import { useRegistry } from '@dxos/react-client/echo';
|
|
17
18
|
import { type ChatView } from '@dxos/react-ui-assistant';
|
|
@@ -21,7 +22,7 @@ import * as Panel from '@dxos/react-ui/Panel';
|
|
|
21
22
|
import { Merge } from '@dxos/util';
|
|
22
23
|
|
|
23
24
|
import { Chat as ChatComponent, type ChatRootProps } from '#components';
|
|
24
|
-
import {
|
|
25
|
+
import { useChatModel, useChatServices, usePlatform, usePresets, useSelectionContext } from '#hooks';
|
|
25
26
|
import { AssistantCapabilities } from '#types';
|
|
26
27
|
|
|
27
28
|
export type ChatArticleProps = Merge<
|
|
@@ -47,7 +48,16 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
|
|
|
47
48
|
const runtime = useChatServices({ id: db?.spaceId });
|
|
48
49
|
|
|
49
50
|
const { preset, ...chatProps } = usePresets(settings, chat);
|
|
50
|
-
|
|
51
|
+
// Every prompt carries the member's DID, so what they say is attributed to them rather than to a name.
|
|
52
|
+
const identity = useIdentity();
|
|
53
|
+
const sender = useMemo(
|
|
54
|
+
() =>
|
|
55
|
+
identity
|
|
56
|
+
? { identityDid: identity.did, ...(identity.displayName ? { name: identity.displayName } : {}) }
|
|
57
|
+
: undefined,
|
|
58
|
+
[identity?.did, identity?.displayName],
|
|
59
|
+
);
|
|
60
|
+
const chatModel = useChatModel({ db, chat, preset, runtime, registry, settings, sender });
|
|
51
61
|
const getContext = useSelectionContext(companionTo);
|
|
52
62
|
|
|
53
63
|
// Subscribe to the view type via `useObject` so the thread re-renders when ChatOptions changes it;
|
|
@@ -85,7 +95,7 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
|
|
|
85
95
|
}, [attendableId]);
|
|
86
96
|
|
|
87
97
|
useEffect(() => {
|
|
88
|
-
if (!
|
|
98
|
+
if (!chatModel || !attendableId || pendingSubmitted.current) {
|
|
89
99
|
return;
|
|
90
100
|
}
|
|
91
101
|
|
|
@@ -98,15 +108,15 @@ export const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(
|
|
|
98
108
|
return { ...current, pendingPrompts: rest };
|
|
99
109
|
});
|
|
100
110
|
|
|
101
|
-
void
|
|
111
|
+
void chatModel.request({ message: pendingPrompt });
|
|
102
112
|
}
|
|
103
|
-
}, [
|
|
113
|
+
}, [chatModel, attendableId, atomRegistry, stateAtom]);
|
|
104
114
|
|
|
105
115
|
return (
|
|
106
116
|
<ChatComponent.Root
|
|
107
117
|
chat={chat}
|
|
108
118
|
db={db}
|
|
109
|
-
|
|
119
|
+
chatModel={chatModel}
|
|
110
120
|
debug={debug}
|
|
111
121
|
getContext={getContext}
|
|
112
122
|
onEvent={onEvent}
|