@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
|
@@ -7,7 +7,8 @@ import * as Hooks from "@dxos/app-framework/Hooks";
|
|
|
7
7
|
import { Obj } from "@dxos/echo";
|
|
8
8
|
import { useObject } from "@dxos/echo-react";
|
|
9
9
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import {
|
|
10
|
+
import { useChatModel, useChatServices, usePlatform, usePresets, useSelectionContext } from "#hooks";
|
|
11
|
+
import { useIdentity } from "@dxos/halo-react";
|
|
11
12
|
import { graphActions, isPromptAction } from "@dxos/react-ui-menu";
|
|
12
13
|
import * as Layout from "@dxos/react-ui/Layout";
|
|
13
14
|
import * as ToolkitHooks from "@dxos/app-toolkit/Hooks";
|
|
@@ -25,13 +26,19 @@ var ChatArticle = forwardRef(({ role, attendableId, nodeId, subject: chat, compa
|
|
|
25
26
|
const db = (chat && Obj.getDatabase(chat)) ?? (companionTo && Obj.getDatabase(companionTo));
|
|
26
27
|
const runtime = useChatServices({ id: db?.spaceId });
|
|
27
28
|
const { preset, ...chatProps } = usePresets(settings, chat);
|
|
28
|
-
const
|
|
29
|
+
const identity = useIdentity();
|
|
30
|
+
const sender = useMemo(() => identity ? {
|
|
31
|
+
identityDid: identity.did,
|
|
32
|
+
...identity.displayName ? { name: identity.displayName } : {}
|
|
33
|
+
} : void 0, [identity?.did, identity?.displayName]);
|
|
34
|
+
const chatModel = useChatModel({
|
|
29
35
|
db,
|
|
30
36
|
chat,
|
|
31
37
|
preset,
|
|
32
38
|
runtime,
|
|
33
39
|
registry,
|
|
34
|
-
settings
|
|
40
|
+
settings,
|
|
41
|
+
sender
|
|
35
42
|
});
|
|
36
43
|
const getContext = useSelectionContext(companionTo);
|
|
37
44
|
const [chatViewType] = useObject(chat, "viewType");
|
|
@@ -48,7 +55,7 @@ var ChatArticle = forwardRef(({ role, attendableId, nodeId, subject: chat, compa
|
|
|
48
55
|
pendingSubmitted.current = false;
|
|
49
56
|
}, [attendableId]);
|
|
50
57
|
useEffect(() => {
|
|
51
|
-
if (!
|
|
58
|
+
if (!chatModel || !attendableId || pendingSubmitted.current) return;
|
|
52
59
|
const pendingPrompt = atomRegistry.get(stateAtom).pendingPrompts[attendableId];
|
|
53
60
|
if (pendingPrompt) {
|
|
54
61
|
pendingSubmitted.current = true;
|
|
@@ -59,10 +66,10 @@ var ChatArticle = forwardRef(({ role, attendableId, nodeId, subject: chat, compa
|
|
|
59
66
|
pendingPrompts: rest
|
|
60
67
|
};
|
|
61
68
|
});
|
|
62
|
-
|
|
69
|
+
chatModel.request({ message: pendingPrompt });
|
|
63
70
|
}
|
|
64
71
|
}, [
|
|
65
|
-
|
|
72
|
+
chatModel,
|
|
66
73
|
attendableId,
|
|
67
74
|
atomRegistry,
|
|
68
75
|
stateAtom
|
|
@@ -70,7 +77,7 @@ var ChatArticle = forwardRef(({ role, attendableId, nodeId, subject: chat, compa
|
|
|
70
77
|
return /* @__PURE__ */ jsx(Chat.Root, {
|
|
71
78
|
chat,
|
|
72
79
|
db,
|
|
73
|
-
|
|
80
|
+
chatModel,
|
|
74
81
|
debug,
|
|
75
82
|
getContext,
|
|
76
83
|
onEvent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-ChatArticle.mjs","names":[],"sources":["../../src/containers/ChatArticle/ChatArticle.tsx","../../src/containers/ChatArticle/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\nimport React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport * as ToolkitHooks from '@dxos/app-toolkit/Hooks';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport { Obj } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport * as ClientOperation from '@dxos/plugin-client/ClientOperation';\nimport { useRegistry } from '@dxos/react-client/echo';\nimport { type ChatView } from '@dxos/react-ui-assistant';\nimport { graphActions, isPromptAction } from '@dxos/react-ui-menu';\nimport * as Layout from '@dxos/react-ui/Layout';\nimport * as Panel from '@dxos/react-ui/Panel';\nimport { Merge } from '@dxos/util';\n\nimport { Chat as ChatComponent, type ChatRootProps } from '#components';\nimport { useChatProcessor, useChatServices, usePlatform, usePresets, useSelectionContext } from '#hooks';\nimport { AssistantCapabilities } from '#types';\n\nexport type ChatArticleProps = Merge<\n Omit<AppSurface.ObjectSectionProps<Chat.Chat>, 'subject'> & {\n subject?: Chat.Chat;\n companionTo?: Obj.Unknown;\n },\n Pick<ChatRootProps, 'debug' | 'onEvent' | 'onSubmit'>\n>;\n\nexport const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(\n ({ role, attendableId, nodeId, subject: chat, companionTo, debug, onEvent, onSubmit }, forwardedRef) => {\n const registry = useRegistry();\n // The marker rail is a hover/precision target pinned to the thread's left edge, and the status\n // pill floats over the last turn — on a phone the rail has nowhere to live outside the text and\n // the pill covers the reply it reports on. Neither has a toggle to orphan; both are passive.\n const mobile = usePlatform() === 'mobile';\n const settings = Hooks.useAtomCapability(AssistantCapabilities.Settings);\n const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);\n const stateAtom = Hooks.useCapability(AssistantCapabilities.State);\n // Transient (pre-submit) chats have no database; fall back to the companion's.\n const db = (chat && Obj.getDatabase(chat)) ?? (companionTo && Obj.getDatabase(companionTo));\n const runtime = useChatServices({ id: db?.spaceId });\n\n const { preset, ...chatProps } = usePresets(settings, chat);\n const processor = useChatProcessor({ db, chat, preset, runtime, registry, settings });\n const getContext = useSelectionContext(companionTo);\n\n // Subscribe to the view type via `useObject` so the thread re-renders when ChatOptions changes it;\n // a direct `chat.viewType` read in render does not establish a reactive dependency.\n const [chatViewType] = useObject(chat, 'viewType');\n const viewType = (chatViewType as ChatView | undefined) ?? settings.chatView;\n\n const { invokePromise } = Hooks.useOperationInvoker();\n const handleViewUsage = useCallback(() => {\n void invokePromise(ClientOperation.OpenUsage, undefined);\n }, [invokePromise]);\n\n // Actions other plugins filed on this chat's node — the microphone among them — so a contributor\n // reaches the prompt without plugin-assistant importing it (or knowing it exists).\n //\n // Sourced from this surface's own node rather than `attendableId`: a companion shares the host\n // plank's attention id (`CompanionPlank`), so reading actions from it hands a chat attached to a\n // document that document's toolbar — its comment action, and a second copy of its microphone\n // keyed to the document. `nodeId` is the companion node itself, which is where a contributor\n // matching on the chat files them; it falls back to the object for a surface rendered outside a\n // plank. Filtered to the prompt surface for the same reason the id is: an action on the chat\n // acts on the chat, and only some of those belong beside the text being composed.\n const { graph } = ToolkitHooks.useAppGraph();\n const actionNodeId = nodeId ?? (chat && Obj.getURI(chat));\n const customActions = useMemo(\n () => Atom.make((get) => graphActions(graph, get, actionNodeId, { filter: isPromptAction })),\n [graph, actionNodeId],\n );\n\n // Reset the one-shot guard when the target conversation changes, so a pending prompt for a new\n // `attendableId` is still auto-submitted within the same mount.\n const pendingSubmitted = useRef(false);\n useEffect(() => {\n pendingSubmitted.current = false;\n }, [attendableId]);\n\n useEffect(() => {\n if (!processor || !attendableId || pendingSubmitted.current) {\n return;\n }\n\n const state = atomRegistry.get(stateAtom);\n const pendingPrompt = state.pendingPrompts[attendableId];\n if (pendingPrompt) {\n pendingSubmitted.current = true;\n atomRegistry.update(stateAtom, (current) => {\n const { [attendableId]: _, ...rest } = current.pendingPrompts;\n return { ...current, pendingPrompts: rest };\n });\n\n void processor.request({ message: pendingPrompt });\n }\n }, [processor, attendableId, atomRegistry, stateAtom]);\n\n return (\n <ChatComponent.Root\n chat={chat}\n db={db}\n processor={processor}\n debug={debug}\n getContext={getContext}\n onEvent={onEvent}\n onSubmit={onSubmit}\n >\n <Panel.Root role={role} ref={forwardedRef}>\n <Panel.Header>\n <ChatComponent.Toolbar classNames='dx-document' attendableId={attendableId} companionTo={companionTo} />\n </Panel.Header>\n <Panel.Body asChild>\n <ChatComponent.Content>\n <Layout.Flex classNames='dx-expand relative'>\n {/* Thread outline (Table of Contents). */}\n {!mobile && <ChatComponent.Outline classNames='absolute left-0 top-1/2 -translate-y-1/2 z-10' />}\n\n {/* Main thread. */}\n <ChatComponent.Thread viewType={viewType} tailLines={4} onViewUsage={handleViewUsage} />\n\n {/** Floating info. */}\n {!mobile && (\n <Layout.Grid\n cols={['fill', 'auto']}\n gap='sm'\n classNames='absolute bottom-0 left-0 right-0 dx-document px-3 pb-2'\n data-testid='assistant.chat-status'\n >\n {/* Pinned to their columns: either renders nothing while idle, which would move the other over. */}\n <ChatComponent.Activity classNames='col-start-1 self-center' />\n <ChatComponent.Status classNames='col-start-2 justify-self-end bg-input-surface rounded-sm' />\n </Layout.Grid>\n )}\n </Layout.Flex>\n\n <Layout.Flex column classNames='dx-document px-2 pb-2'>\n {mobile && (\n <Layout.Grid cols={2}>\n <ChatComponent.Activity />\n </Layout.Grid>\n )}\n\n {/* Composer and checklist in one: `Chat.Prompt` owns the disclosure between them. */}\n <ChatComponent.Prompt\n {...chatProps}\n outline\n autoFocus={!companionTo}\n attendableId={attendableId}\n companionTo={companionTo}\n customActions={customActions}\n nodeId={actionNodeId}\n preset={preset?.id}\n />\n </Layout.Flex>\n </ChatComponent.Content>\n </Panel.Body>\n </Panel.Root>\n </ChatComponent.Root>\n );\n },\n);\n\nChatArticle.displayName = 'ChatArticle';\n","//\n// Copyright 2025 DXOS.org\n//\n\nexport { ChatArticle as default } from './ChatArticle.tsx';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chunk-ChatArticle.mjs","names":[],"sources":["../../src/containers/ChatArticle/ChatArticle.tsx","../../src/containers/ChatArticle/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\nimport React, { forwardRef, useCallback, useEffect, useMemo, useRef } from 'react';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport * as ToolkitHooks from '@dxos/app-toolkit/Hooks';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport { Obj } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { useIdentity } from '@dxos/halo-react';\nimport * as ClientOperation from '@dxos/plugin-client/ClientOperation';\nimport { useRegistry } from '@dxos/react-client/echo';\nimport { type ChatView } from '@dxos/react-ui-assistant';\nimport { graphActions, isPromptAction } from '@dxos/react-ui-menu';\nimport * as Layout from '@dxos/react-ui/Layout';\nimport * as Panel from '@dxos/react-ui/Panel';\nimport { Merge } from '@dxos/util';\n\nimport { Chat as ChatComponent, type ChatRootProps } from '#components';\nimport { useChatModel, useChatServices, usePlatform, usePresets, useSelectionContext } from '#hooks';\nimport { AssistantCapabilities } from '#types';\n\nexport type ChatArticleProps = Merge<\n Omit<AppSurface.ObjectSectionProps<Chat.Chat>, 'subject'> & {\n subject?: Chat.Chat;\n companionTo?: Obj.Unknown;\n },\n Pick<ChatRootProps, 'debug' | 'onEvent' | 'onSubmit'>\n>;\n\nexport const ChatArticle = forwardRef<HTMLDivElement, ChatArticleProps>(\n ({ role, attendableId, nodeId, subject: chat, companionTo, debug, onEvent, onSubmit }, forwardedRef) => {\n const registry = useRegistry();\n // The marker rail is a hover/precision target pinned to the thread's left edge, and the status\n // pill floats over the last turn — on a phone the rail has nowhere to live outside the text and\n // the pill covers the reply it reports on. Neither has a toggle to orphan; both are passive.\n const mobile = usePlatform() === 'mobile';\n const settings = Hooks.useAtomCapability(AssistantCapabilities.Settings);\n const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);\n const stateAtom = Hooks.useCapability(AssistantCapabilities.State);\n // Transient (pre-submit) chats have no database; fall back to the companion's.\n const db = (chat && Obj.getDatabase(chat)) ?? (companionTo && Obj.getDatabase(companionTo));\n const runtime = useChatServices({ id: db?.spaceId });\n\n const { preset, ...chatProps } = usePresets(settings, chat);\n // Every prompt carries the member's DID, so what they say is attributed to them rather than to a name.\n const identity = useIdentity();\n const sender = useMemo(\n () =>\n identity\n ? { identityDid: identity.did, ...(identity.displayName ? { name: identity.displayName } : {}) }\n : undefined,\n [identity?.did, identity?.displayName],\n );\n const chatModel = useChatModel({ db, chat, preset, runtime, registry, settings, sender });\n const getContext = useSelectionContext(companionTo);\n\n // Subscribe to the view type via `useObject` so the thread re-renders when ChatOptions changes it;\n // a direct `chat.viewType` read in render does not establish a reactive dependency.\n const [chatViewType] = useObject(chat, 'viewType');\n const viewType = (chatViewType as ChatView | undefined) ?? settings.chatView;\n\n const { invokePromise } = Hooks.useOperationInvoker();\n const handleViewUsage = useCallback(() => {\n void invokePromise(ClientOperation.OpenUsage, undefined);\n }, [invokePromise]);\n\n // Actions other plugins filed on this chat's node — the microphone among them — so a contributor\n // reaches the prompt without plugin-assistant importing it (or knowing it exists).\n //\n // Sourced from this surface's own node rather than `attendableId`: a companion shares the host\n // plank's attention id (`CompanionPlank`), so reading actions from it hands a chat attached to a\n // document that document's toolbar — its comment action, and a second copy of its microphone\n // keyed to the document. `nodeId` is the companion node itself, which is where a contributor\n // matching on the chat files them; it falls back to the object for a surface rendered outside a\n // plank. Filtered to the prompt surface for the same reason the id is: an action on the chat\n // acts on the chat, and only some of those belong beside the text being composed.\n const { graph } = ToolkitHooks.useAppGraph();\n const actionNodeId = nodeId ?? (chat && Obj.getURI(chat));\n const customActions = useMemo(\n () => Atom.make((get) => graphActions(graph, get, actionNodeId, { filter: isPromptAction })),\n [graph, actionNodeId],\n );\n\n // Reset the one-shot guard when the target conversation changes, so a pending prompt for a new\n // `attendableId` is still auto-submitted within the same mount.\n const pendingSubmitted = useRef(false);\n useEffect(() => {\n pendingSubmitted.current = false;\n }, [attendableId]);\n\n useEffect(() => {\n if (!chatModel || !attendableId || pendingSubmitted.current) {\n return;\n }\n\n const state = atomRegistry.get(stateAtom);\n const pendingPrompt = state.pendingPrompts[attendableId];\n if (pendingPrompt) {\n pendingSubmitted.current = true;\n atomRegistry.update(stateAtom, (current) => {\n const { [attendableId]: _, ...rest } = current.pendingPrompts;\n return { ...current, pendingPrompts: rest };\n });\n\n void chatModel.request({ message: pendingPrompt });\n }\n }, [chatModel, attendableId, atomRegistry, stateAtom]);\n\n return (\n <ChatComponent.Root\n chat={chat}\n db={db}\n chatModel={chatModel}\n debug={debug}\n getContext={getContext}\n onEvent={onEvent}\n onSubmit={onSubmit}\n >\n <Panel.Root role={role} ref={forwardedRef}>\n <Panel.Header>\n <ChatComponent.Toolbar classNames='dx-document' attendableId={attendableId} companionTo={companionTo} />\n </Panel.Header>\n <Panel.Body asChild>\n <ChatComponent.Content>\n <Layout.Flex classNames='dx-expand relative'>\n {/* Thread outline (Table of Contents). */}\n {!mobile && <ChatComponent.Outline classNames='absolute left-0 top-1/2 -translate-y-1/2 z-10' />}\n\n {/* Main thread. */}\n <ChatComponent.Thread viewType={viewType} tailLines={4} onViewUsage={handleViewUsage} />\n\n {/** Floating info. */}\n {!mobile && (\n <Layout.Grid\n cols={['fill', 'auto']}\n gap='sm'\n classNames='absolute bottom-0 left-0 right-0 dx-document px-3 pb-2'\n data-testid='assistant.chat-status'\n >\n {/* Pinned to their columns: either renders nothing while idle, which would move the other over. */}\n <ChatComponent.Activity classNames='col-start-1 self-center' />\n <ChatComponent.Status classNames='col-start-2 justify-self-end bg-input-surface rounded-sm' />\n </Layout.Grid>\n )}\n </Layout.Flex>\n\n <Layout.Flex column classNames='dx-document px-2 pb-2'>\n {mobile && (\n <Layout.Grid cols={2}>\n <ChatComponent.Activity />\n </Layout.Grid>\n )}\n\n {/* Composer and checklist in one: `Chat.Prompt` owns the disclosure between them. */}\n <ChatComponent.Prompt\n {...chatProps}\n outline\n autoFocus={!companionTo}\n attendableId={attendableId}\n companionTo={companionTo}\n customActions={customActions}\n nodeId={actionNodeId}\n preset={preset?.id}\n />\n </Layout.Flex>\n </ChatComponent.Content>\n </Panel.Body>\n </Panel.Root>\n </ChatComponent.Root>\n );\n },\n);\n\nChatArticle.displayName = 'ChatArticle';\n","//\n// Copyright 2025 DXOS.org\n//\n\nexport { ChatArticle as default } from './ChatArticle.tsx';\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAmCA,IAAa,cAAc,YACxB,EAAE,MAAM,cAAc,QAAQ,SAAS,MAAM,aAAa,OAAO,SAAS,YAAY,iBAAiB;CACtG,MAAM,WAAW,YAAY;CAI7B,MAAM,SAAS,YAAY,MAAM;CACjC,MAAM,WAAW,MAAM,kBAAkB,sBAAsB,QAAQ;CACvE,MAAM,eAAe,MAAM,cAAc,aAAa,YAAY;CAClE,MAAM,YAAY,MAAM,cAAc,sBAAsB,KAAK;CAEjE,MAAM,MAAM,QAAQ,IAAI,YAAY,IAAI,OAAO,eAAe,IAAI,YAAY,WAAW;CACzF,MAAM,UAAU,gBAAgB,EAAE,IAAI,IAAI,QAAQ,CAAC;CAEnD,MAAM,EAAE,QAAQ,GAAG,cAAc,WAAW,UAAU,IAAI;CAE1D,MAAM,WAAW,YAAY;CAC7B,MAAM,SAAS,cAEX,WACI;EAAE,aAAa,SAAS;EAAK,GAAI,SAAS,cAAc,EAAE,MAAM,SAAS,YAAY,IAAI,CAAC;CAAG,IAC7F,KAAA,GACN,CAAC,UAAU,KAAK,UAAU,WAAW,CACvC;CACA,MAAM,YAAY,aAAa;EAAE;EAAI;EAAM;EAAQ;EAAS;EAAU;EAAU;CAAO,CAAC;CACxF,MAAM,aAAa,oBAAoB,WAAW;CAIlD,MAAM,CAAC,gBAAgB,UAAU,MAAM,UAAU;CACjD,MAAM,WAAY,gBAAyC,SAAS;CAEpE,MAAM,EAAE,kBAAkB,MAAM,oBAAoB;CACpD,MAAM,kBAAkB,kBAAkB;EACxC,cAAmB,gBAAgB,WAAW,KAAA,CAAS;CACzD,GAAG,CAAC,aAAa,CAAC;CAYlB,MAAM,EAAE,UAAU,aAAa,YAAY;CAC3C,MAAM,eAAe,WAAW,QAAQ,IAAI,OAAO,IAAI;CACvD,MAAM,gBAAgB,cACd,KAAK,MAAM,QAAQ,aAAa,OAAO,KAAK,cAAc,EAAE,QAAQ,eAAe,CAAC,CAAC,GAC3F,CAAC,OAAO,YAAY,CACtB;CAIA,MAAM,mBAAmB,OAAO,KAAK;CACrC,gBAAgB;EACd,iBAAiB,UAAU;CAC7B,GAAG,CAAC,YAAY,CAAC;CAEjB,gBAAgB;EACd,IAAI,CAAC,aAAa,CAAC,gBAAgB,iBAAiB,SAClD;EAIF,MAAM,gBADQ,aAAa,IAAI,SACT,CAAA,CAAM,eAAe;EAC3C,IAAI,eAAe;GACjB,iBAAiB,UAAU;GAC3B,aAAa,OAAO,YAAY,YAAY;IAC1C,MAAM,GAAG,eAAe,GAAG,GAAG,SAAS,QAAQ;IAC/C,OAAO;KAAE,GAAG;KAAS,gBAAgB;IAAK;GAC5C,CAAC;GAED,UAAe,QAAQ,EAAE,SAAS,cAAc,CAAC;EACnD;CACF,GAAG;EAAC;EAAW;EAAc;EAAc;CAAS,CAAC;CAErD,OACE,oBAAC,KAAc,MAAf;EACQ;EACF;EACO;EACJ;EACK;EACH;EACC;EAEV,UAAA,qBAAC,MAAM,MAAP;GAAkB;GAAM,KAAK;GAA7B,UAAA,CACE,oBAAC,MAAM,QAAP,EAAA,UACE,oBAAC,KAAc,SAAf;IAAuB,YAAW;IAA4B;IAA2B;GAAc,CAAA,EAC3F,CAAA,GACd,oBAAC,MAAM,MAAP;IAAY,SAAA;IACV,UAAA,qBAAC,KAAc,SAAf,EAAA,UAAA,CACE,qBAAC,OAAO,MAAR;KAAa,YAAW;KAAxB,UAAA;MAEG,CAAC,UAAU,oBAAC,KAAc,SAAf,EAAuB,YAAW,gDAAiD,CAAA;MAG/F,oBAAC,KAAc,QAAf;OAAgC;OAAU,WAAW;OAAG,aAAa;MAAkB,CAAA;MAGtF,CAAC,UACA,qBAAC,OAAO,MAAR;OACE,MAAM,CAAC,QAAQ,MAAM;OACrB,KAAI;OACJ,YAAW;OACX,eAAY;OAJd,UAAA,CAOE,oBAAC,KAAc,UAAf,EAAwB,YAAW,0BAA2B,CAAA,GAC9D,oBAAC,KAAc,QAAf,EAAsB,YAAW,2DAA4D,CAAA,CAClF;;KAEJ;IAEb,CAAA,GAAA,qBAAC,OAAO,MAAR;KAAa,QAAA;KAAO,YAAW;KAA/B,UAAA,CACG,UACC,oBAAC,OAAO,MAAR;MAAa,MAAM;MACjB,UAAA,oBAAC,KAAc,UAAf,CAAyB,CAAA;KACd,CAAA,GAIf,oBAAC,KAAc,QAAf;MACE,GAAI;MACJ,SAAA;MACA,WAAW,CAAC;MACE;MACD;MACE;MACf,QAAQ;MACR,QAAQ,QAAQ;KACjB,CAAA,CACU;IACQ,CAAA,CAAA,EAAA,CAAA;GACb,CAAA,CACF;;CACM,CAAA;AAExB,CACF;AAEA,YAAY,cAAc"}
|
|
@@ -5,7 +5,7 @@ import * as Hooks from "@dxos/app-framework/Hooks";
|
|
|
5
5
|
import { Obj } from "@dxos/echo";
|
|
6
6
|
import { useObject } from "@dxos/echo-react";
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
import {
|
|
8
|
+
import { useChatModel, useChatServices, usePresets } from "#hooks";
|
|
9
9
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
10
10
|
import { ChatDialog as ChatDialog$1 } from "@dxos/react-ui-chat";
|
|
11
11
|
import { Chat } from "#components";
|
|
@@ -18,7 +18,8 @@ var ChatDialog = ({ chat }) => {
|
|
|
18
18
|
const runtime = useChatServices({ id: db?.spaceId });
|
|
19
19
|
const { preset, ...chatProps } = usePresets(settings, chat);
|
|
20
20
|
const registry = useRegistry();
|
|
21
|
-
const
|
|
21
|
+
const chatModel = useChatModel({
|
|
22
|
+
db,
|
|
22
23
|
chat,
|
|
23
24
|
preset,
|
|
24
25
|
runtime,
|
|
@@ -38,10 +39,10 @@ var ChatDialog = ({ chat }) => {
|
|
|
38
39
|
case "thread-close": setOpen(false);
|
|
39
40
|
}
|
|
40
41
|
}, []);
|
|
41
|
-
if (!chat || !
|
|
42
|
+
if (!chat || !chatModel) return null;
|
|
42
43
|
return /* @__PURE__ */ jsx(Chat.Root, {
|
|
43
44
|
chat,
|
|
44
|
-
|
|
45
|
+
chatModel,
|
|
45
46
|
onEvent: handleEvent,
|
|
46
47
|
children: /* @__PURE__ */ jsxs(ChatDialog$1.Root, {
|
|
47
48
|
open,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-ChatDialog.mjs","names":[],"sources":["../../src/containers/ChatDialog/ChatDialog.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AssistantChat from '@dxos/assistant/Chat';\nimport { Obj } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { useRegistry } from '@dxos/react-client/echo';\nimport { type ChatView } from '@dxos/react-ui-assistant';\nimport { ChatDialog as NaturalChatDialog } from '@dxos/react-ui-chat';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { Chat, type ChatRootProps } from '#components';\nimport {
|
|
1
|
+
{"version":3,"file":"chunk-ChatDialog.mjs","names":[],"sources":["../../src/containers/ChatDialog/ChatDialog.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AssistantChat from '@dxos/assistant/Chat';\nimport { Obj } from '@dxos/echo';\nimport { useObject } from '@dxos/echo-react';\nimport { useRegistry } from '@dxos/react-client/echo';\nimport { type ChatView } from '@dxos/react-ui-assistant';\nimport { ChatDialog as NaturalChatDialog } from '@dxos/react-ui-chat';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { Chat, type ChatRootProps } from '#components';\nimport { useChatModel, useChatServices, usePresets } from '#hooks';\nimport { meta } from '#meta';\nimport { AssistantCapabilities } from '#types';\n\nexport type ChatDialogProps = {\n chat?: AssistantChat.Chat;\n};\n\nexport const ChatDialog = ({ chat }: ChatDialogProps) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n\n const db = chat && Obj.getDatabase(chat);\n const settings = Hooks.useAtomCapability(AssistantCapabilities.Settings);\n const runtime = useChatServices({ id: db?.spaceId });\n const { preset, ...chatProps } = usePresets(settings, chat);\n const registry = useRegistry();\n const chatModel = useChatModel({ db, chat, preset, runtime, registry, settings });\n // Subscribe via `useObject` so the thread re-renders when ChatOptions changes the view type.\n const [chatViewType] = useObject(chat, 'viewType');\n\n // TODO(burdon): Refocus when open.\n const [open, setOpen] = useState(true);\n const [expanded, setExpanded] = useState(false);\n const handleEvent = useCallback<NonNullable<ChatRootProps['onEvent']>>((event) => {\n switch (event.type) {\n case 'submit':\n case 'thread-open':\n setOpen(true);\n setExpanded(true);\n break;\n case 'thread-close':\n setOpen(false);\n break;\n }\n }, []);\n\n if (!chat || !chatModel) {\n return null;\n }\n\n return (\n <Chat.Root chat={chat} chatModel={chatModel} onEvent={handleEvent}>\n <NaturalChatDialog.Root open={open} expanded={expanded} onOpenChange={setOpen}>\n <NaturalChatDialog.Header title={t('assistant-dialog.title')} />\n <NaturalChatDialog.Content>\n <Chat.Thread viewType={(chatViewType as ChatView | undefined) ?? settings.chatView} />\n </NaturalChatDialog.Content>\n <NaturalChatDialog.Footer classNames='p-1.5'>\n {/* What the request is doing before the first token arrives. */}\n <Chat.Activity />\n {/* Queued prompts the agent has not taken up yet, stacked right above the composer. */}\n <Chat.Prompt {...chatProps} preset={preset?.id} expandable />\n </NaturalChatDialog.Footer>\n </NaturalChatDialog.Root>\n </Chat.Root>\n );\n};\n\nChatDialog.displayName = 'ChatDialog';\n"],"mappings":";;;;;;;;;;;;;AAwBA,IAAa,cAAc,EAAE,WAA4B;CACvD,MAAM,EAAE,MAAM,QAAQ,eAAe,KAAK,QAAQ,GAAG;CAErD,MAAM,KAAK,QAAQ,IAAI,YAAY,IAAI;CACvC,MAAM,WAAW,MAAM,kBAAkB,sBAAsB,QAAQ;CACvE,MAAM,UAAU,gBAAgB,EAAE,IAAI,IAAI,QAAQ,CAAC;CACnD,MAAM,EAAE,QAAQ,GAAG,cAAc,WAAW,UAAU,IAAI;CAC1D,MAAM,WAAW,YAAY;CAC7B,MAAM,YAAY,aAAa;EAAE;EAAI;EAAM;EAAQ;EAAS;EAAU;CAAS,CAAC;CAEhF,MAAM,CAAC,gBAAgB,UAAU,MAAM,UAAU;CAGjD,MAAM,CAAC,MAAM,WAAW,SAAS,IAAI;CACrC,MAAM,CAAC,UAAU,eAAe,SAAS,KAAK;CAC9C,MAAM,cAAc,aAAoD,UAAU;EAChF,QAAQ,MAAM,MAAd;GACE,KAAK;GACL,KAAK;IACH,QAAQ,IAAI;IACZ,YAAY,IAAI;IAChB;GACF,KAAK,gBACH,QAAQ,KAAK;EAEjB;CACF,GAAG,CAAC,CAAC;CAEL,IAAI,CAAC,QAAQ,CAAC,WACZ,OAAO;CAGT,OACE,oBAAC,KAAK,MAAN;EAAiB;EAAiB;EAAW,SAAS;EACpD,UAAA,qBAAC,aAAkB,MAAnB;GAA8B;GAAgB;GAAU,cAAc;GAAtE,UAAA;IACE,oBAAC,aAAkB,QAAnB,EAA0B,OAAO,EAAE,wBAAwB,EAAI,CAAA;IAC/D,oBAAC,aAAkB,SAAnB,EAAA,UACE,oBAAC,KAAK,QAAN,EAAa,UAAW,gBAAyC,SAAS,SAAW,CAAA,EAC5D,CAAA;IAC3B,qBAAC,aAAkB,QAAnB;KAA0B,YAAW;KAArC,UAAA,CAEE,oBAAC,KAAK,UAAN,CAAgB,CAAA,GAEhB,oBAAC,KAAK,QAAN;MAAa,GAAI;MAAW,QAAQ,QAAQ;MAAI,YAAA;KAAY,CAAA,CACpC;;GACJ;;CACf,CAAA;AAEf;AAEA,WAAW,cAAc"}
|
|
@@ -7,8 +7,8 @@ import { Filter } from "@dxos/echo";
|
|
|
7
7
|
import { useQuery } from "@dxos/echo-react";
|
|
8
8
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
10
|
-
import { Connection } from "@dxos/link";
|
|
11
10
|
import * as Layout from "@dxos/react-ui/Layout";
|
|
11
|
+
import { Connection } from "@dxos/link";
|
|
12
12
|
import * as Icon from "@dxos/react-ui/Icon";
|
|
13
13
|
import * as ToolkitHooks from "@dxos/app-toolkit/Hooks";
|
|
14
14
|
import { ConnectorAuthMenu } from "#components";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region PLUGIN.mdl?raw
|
|
2
|
-
var PLUGIN_default = "---\nid: org.dxos.plugin.assistant\nname: AssistantPlugin\nversion: 0.1.0\n---\n\nAn AI assistant plugin for DXOS Composer that provides conversational AI, skill-driven automation,\nmulti-provider LLM support, and deep ECHO integration.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|-------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `scenario` | `org.dxos.mdl.scenario@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.1` |\n| `test` | `org.dxos.mdl.test@1.0` |\n\n## Types\n\n```mdl\ntype ModelProvider\n literals: edge | ollama | lmstudio\n```\n\n```mdl\ntype ChatView\n literals: normal | summary | thinking | debug\n```\n\n```mdl\ntype ModelDefaults\n fields:\n edge?: string\n ollama?: string\n lmstudio?: string\n```\n\n```mdl\ntype Settings\n fields:\n customPrompts?: boolean\n chatView?: ChatView\n modelProvider?: ModelProvider\n modelDefaults?: ModelDefaults\n codeMode?: boolean\n tracePanelDebug?: boolean\n```\n\n```mdl\ntype Chat\n fields:\n id: string\n name?: string\n messages: Message[]\n```\n\n```mdl\ntype Message\n fields:\n id: string\n role: user | assistant | system\n content: string\n timestamp: string\n```\n\n```mdl\ntype SkillForm\n fields:\n key: string\n name: string\n description?: string\n```\n\n## Components\n\n```mdl\ncomponent ChatPanel\n desc: Main conversational interface for interacting with the AI assistant.\n props:\n chat: Chat\n companionTo?: Obj\n state:\n messages: Message[]\n pending: boolean\n view: ChatView\n slots:\n toolbar?: ReactNode\n empty?: ReactNode\n actions:\n sendMessage(content: string)\n clearHistory()\n switchView(view: ChatView)\n emits:\n onMessage(message: Message)\n layout: |\n ┌─────────────────────────┐\n │ [toolbar slot] │\n ├─────────────────────────┤\n │ │\n │ message list │\n │ [empty slot] │\n │ │\n ├─────────────────────────┤\n │ input + send │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent TracePanel\n desc: Displays the execution trace of an AI operation as a commit graph or raw JSON.\n props:\n chat: Chat\n state:\n debug: boolean\n actions:\n toggleDebug()\n layout: |\n ┌─────────────────────────┐\n │ trace / span tree │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent AssistantSettings\n desc: Settings panel for configuring the AI model provider, model defaults, and chat view.\n props:\n settings: Settings\n emits:\n onChange(settings: Settings)\n```\n\n## Operations\n\n```mdl\nop CreateChat\n key: org.dxos.operation.assistant.createChat\n desc: Creates a new Chat object and optionally adds it to the active space.\n input:\n db: Database\n name?: string\n addToSpace?: boolean\n output: Chat\n effects: [echo:write]\n```\n\n```mdl\nop UpdateChatName\n key: org.dxos.operation.assistant.updateChatName\n desc: Automatically derives and updates the name of a chat using the AI service.\n input:\n chat: Chat\n output: void\n effects: [echo:write, ai:infer]\n```\n\n```mdl\nop SetCurrentChat\n desc: Associates a primary object with a specific companion chat in session state.\n input:\n companionTo: Obj\n chat?: Chat\n output: void\n```\n\n```mdl\nop RunPromptInNewChat\n desc: Creates a new chat, binds context objects and skills, then runs a prompt.\n note: |\n Operation DEFINED in plugin-routine (RoutineOperation.RunPromptInNewChat)\n so Routine UI and downstream plugins can dispatch it without depending on\n plugin-assistant. The HANDLER (chat creation + AiContext binding) is contributed\n here; see operations/run-prompt-in-new-chat.ts.\n```\n\n```mdl\nop EnsureCompanionChat\n key: org.dxos.operation.assistant.ensureCompanionChat\n desc: Returns the companion chat for an object, creating one if none exists.\n input:\n db: Database\n companionTo: Obj\n output:\n chat: Chat\n persisted: boolean\n effects: [echo:write]\n```\n\n```mdl\nop OnCreateSpace\n desc: Provisions initial assistant objects when a new space is created.\n input:\n space: Space\n rootCollection: Collection\n output: void\n effects: [echo:write]\n```\n\n```mdl\nop ToggleTracePanelDebug\n key: org.dxos.operation.assistant.setTracePanelDebug\n desc: Toggles the trace panel between commit-graph and raw JSON debug view.\n input:\n state?: boolean\n output: boolean\n```\n\n## Features\n\n```mdl\nfeat F-1: AI Chat\n\n req F-1.1: User can create a new Chat object in any space.\n req F-1.2: User can type a message and receive a streamed AI response.\n req F-1.3: Chat history is persisted to ECHO and replicated to all peers.\n req F-1.4:\n when: chat has enough messages\n then: chat name is automatically derived from the conversation\n```\n\n```mdl\nfeat F-2: Companion Chat\n\n req F-2.1:\n when: user opens an object that supports companions\n then: a companion chat panel is shown alongside the object\n req F-2.2:\n when: companion chat does not yet exist\n then: op:EnsureCompanionChat creates one and optionally persists it\n req F-2.3:\n when: op:EnsureCompanionChat resolves a companion chat\n then: op:BindChatContext runs every cap:SubjectContext contribution that applies to the primary\n object and binds the merged result — by default the object itself plus the skills its type\n declares via Skill.SkillsAnnotation\n```\n\n```mdl\nfeat F-3: Skill System\n\n req F-3.1: Skills define named AI configurations (instructions, tools, model).\n req F-3.2: Built-in skills include: Assistant, Browser, Database, WebSearch, Discord, Linear, Agent, Planning, Memory, Automation, SkillManager.\n req F-3.3:\n when: user selects a skill for a chat\n then: chat uses that skill's instructions and tool set\n req F-3.4: op:RunPromptInNewChat accepts skill keys to bind before sending the prompt.\n```\n\n```mdl\nfeat F-4: Multi-Provider LLM\n\n req F-4.1: Supported providers: DXOS Edge (remote), Ollama (local), LM Studio (local).\n req F-4.2: User can configure the active provider and per-provider default model in Settings.\n req F-4.3:\n when: the active provider is unavailable\n then: AiService falls back to the next registered resolver\n```\n\n```mdl\nfeat F-5: Tool Execution\n\n req F-5.1: The assistant skill exposes LayoutOperation.Open and NavigationOperation.ResolveNavigationTargets\n as tools. Both are owned elsewhere — the assistant contributes no navigation operation of its\n own, so an agent resolving an object to a path uses the same entry point the UI does.\n req F-5.2: Web search tool is registered via WebSearchToolkit.\n req F-5.3:\n when: AI invokes a tool during a conversation\n then: operation is dispatched via the compute runtime and result returned to the model\n req F-5.4: Code mode is opt-in via `Settings.codeMode`, off by default; the host supplies the engine\n through the `codeModeTurnProducer` plugin option, and without it the setting has no effect.\n req F-5.5:\n when: Settings.codeMode is enabled, the host supplied a code-mode producer, and no AgentTurnProducer is contributed\n then: agent processes spawned afterwards run turns through the code-mode producer (one `eval` tool)\n instead of AiSession\n```\n\n```mdl\nfeat F-6: ECHO Integration\n\n req F-6.1: Chat, Skill, Feed, Message, Memory, Plan, Routine, Agent, McpServer, and Sequence schemas are registered with ECHO. A chat's subject is recorded by Chat.CompanionChatAnnotation on that subject, not by a type.\n req F-6.2:\n when: a new space is created\n then: op:OnCreateSpace provisions default assistant objects\n req F-6.3: AiContext.Binding schema supports binding ECHO objects as named context variables in a conversation.\n```\n\n```mdl\nfeat F-7: Execution Trace\n\n req F-7.1: Every AI operation produces a structured span tree captured in the TracePanel.\n req F-7.2: User can toggle between commit-graph and raw JSON views via op:ToggleTracePanelDebug.\n```\n\n## Acceptance\n\n```mdl\nscenario T-1: Create chat and send message\n given: a space is open and the assistant plugin is active\n when: user creates a new Chat and sends \"Hello\"\n then:\n - Chat object appears in the space\n - AI response is appended to Chat.messages\n - Chat is persisted to ECHO\n```\n\n```mdl\nscenario T-2: Companion chat provisioning\n given: user opens a document with no companion chat\n when: companion panel initializes\n then:\n - op:EnsureCompanionChat creates a new Chat\n - persisted === false until user sends a message\n```\n\n```mdl\nscenario T-3: Skill binding\n given: a chat exists\n when: user binds the \"WebSearch\" skill key\n then:\n - skill instructions are applied to the next AI request\n - web search tool is available in the tool list\n```\n\n```mdl\nscenario T-4: Model provider switch\n given: assistant settings are open\n when: user switches provider from edge to ollama\n then:\n - Settings.modelProvider === ollama\n - next AI request is routed to the Ollama resolver\n```\n\n```mdl\nscenario T-5: Run prompt in background\n given: a database and a prompt string are available\n when: op:RunPromptInNewChat is called with background: true\n then:\n - a new Chat is created and returned\n - the prompt runs via compute runtime without opening the UI\n```\n\n```mdl\nscenario T-7: Trace panel debug toggle\n given: trace panel is showing commit-graph view\n when: op:ToggleTracePanelDebug is invoked with no argument\n then:\n - tracePanelDebug setting flips to true\n - trace panel switches to raw JSON view\n```\n\n## QA\n\nExecutable tests. Each step carries a human instruction (`do`) and, where the step maps to an\ninvocable operation, an agent binding (`invoke`). Report failures by test and step number.\n\n```mdl\ntest QA-1: Create a chat and send a message\n desc: The core assistant round-trip — create a Chat, send a prompt, get a streamed AI response.\n actors: both\n covers: [F-1.1, F-1.2, F-1.3, F-1.4, T-1]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - no Chat named \"QA Chat\" exists — ABORT rather than deleting one this test did not create\n steps:\n - name: Create the chat\n do: Open the assistant panel and click \"New chat\".\n invoke: [op:CreateChat] { name: \"QA Chat\" }\n capture: created\n expect: a new Chat named \"QA Chat\" opens, empty\n assert: return !!$created.object\n note: |\n CreateChat returns the chat UNFILED — the handler adds its feed regardless, but persisting\n it in the space is a separate step (see plugin-space's addObject), which the UI's \"New\n chat\" gesture does implicitly.\n - name: Send a message\n do: Type \"Hello\" into the chat input and press Enter.\n expect: \"Hello\" appears as a user message, followed by a streamed assistant response, and\n the chat is persisted to ECHO and replicated to peers\n note: |\n NO `invoke` — sending a prompt runs through the AI streaming path (`AiSession`), not\n through the operation invoker; there is no `SendMessage` operation to call directly.\n - name: Chat is auto-named\n do: (no gesture — happens automatically once the chat has enough messages)\n invoke: [op:UpdateChatName] { chat: $created.object }\n expect: the chat's name changes from \"QA Chat\" to something derived from the conversation\n assert: return typeof $result === 'undefined' || true;\n note: |\n `UpdateChatName` returns void — verify the effect on `chat.name`, not the return value\n (Execution Rule 3). Included here for completeness; a fast QA pass may treat this as\n `given` already established rather than re-triggering it.\n after:\n - name: Delete the chat\n do: Right-click \"QA Chat\" in the navtree and choose Delete.\n invoke: org.dxos.operation.space.removeObjects { objects: [$created.object], target: $given.space.db }\n expect: the row leaves the navtree and its plank closes if it was open\n```\n\n```mdl\ntest QA-2: Companion chat is provisioned and bound\n desc: op:EnsureCompanionChat + op:BindChatContext — the chat a companioned object gets automatically.\n actors: both\n covers: [F-2.1, F-2.2, F-2.3, T-2]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - subject: a Document in the space that has no companion chat yet\n steps:\n - name: Ensure the companion chat\n do: Open the document; the companion chat panel initializes automatically.\n invoke: [op:EnsureCompanionChat] { companionTo: $given.subject }\n capture: ensured\n expect: a new Chat is returned and NOT yet persisted (`persisted === false`)\n assert: return $result.persisted === false && !!$result.chat;\n - name: Bind chat context\n do: (no separate gesture — panel initialization runs this immediately after ensuring the chat)\n invoke: org.dxos.operation.assistant.bindChatContext { chat: $ensured.chat, subject: $given.subject }\n note: No `op BindChatContext` block is declared above — referenced by its full runtime key.\n expect: the companion chat's context now includes the document (and any skills its type\n declares via Skill.SkillsAnnotation)\n after:\n - name: (nothing to undo)\n do: The companion chat is never persisted unless a message is sent, so nothing was written\n to the space.\n expect: the document still has no persisted companion chat\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap\n literals?: UnionList\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self\n```\n\n```mdl\next scenario\n uri: org.dxos.mdl.scenario@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap\n state?: FieldMap\n slots?: FieldMap\n actions?: ActionMap\n emits?: EventMap\n layout?: CodeBlock\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap\n output?: TypeExpr\n errors?: ErrorMap\n effects?: EffectList\n requires?: ServiceList\n note?: Prose\n```\n";
|
|
2
|
+
var PLUGIN_default = "---\nid: org.dxos.plugin.assistant\nname: AssistantPlugin\nversion: 0.1.0\n---\n\nAn AI assistant plugin for DXOS Composer that provides conversational AI, skill-driven automation,\nmulti-provider LLM support, and deep ECHO integration.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|-------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `scenario` | `org.dxos.mdl.scenario@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.1` |\n| `test` | `org.dxos.mdl.test@1.0` |\n\n## Types\n\n```mdl\ntype ModelProvider\n literals: edge | ollama | lmstudio\n```\n\n```mdl\ntype ChatView\n literals: normal | summary | thinking | debug\n```\n\n```mdl\ntype ModelDefaults\n fields:\n edge?: string\n ollama?: string\n lmstudio?: string\n```\n\n```mdl\ntype Settings\n fields:\n customPrompts?: boolean\n chatView?: ChatView\n modelProvider?: ModelProvider\n modelDefaults?: ModelDefaults\n codeMode?: boolean\n reportStruggles?: boolean\n tracePanelDebug?: boolean\n```\n\n```mdl\ntype Chat\n fields:\n id: string\n name?: string\n messages: Message[]\n```\n\n```mdl\ntype Message\n fields:\n id: string\n role: user | assistant | system\n content: string\n timestamp: string\n```\n\n```mdl\ntype SkillForm\n fields:\n key: string\n name: string\n description?: string\n```\n\n## Components\n\n```mdl\ncomponent ChatPanel\n desc: Main conversational interface for interacting with the AI assistant.\n props:\n chat: Chat\n companionTo?: Obj\n state:\n messages: Message[]\n pending: boolean\n view: ChatView\n slots:\n toolbar?: ReactNode\n empty?: ReactNode\n actions:\n sendMessage(content: string)\n clearHistory()\n switchView(view: ChatView)\n emits:\n onMessage(message: Message)\n layout: |\n ┌─────────────────────────┐\n │ [toolbar slot] │\n ├─────────────────────────┤\n │ │\n │ message list │\n │ [empty slot] │\n │ │\n ├─────────────────────────┤\n │ input + send │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent TracePanel\n desc: Displays the execution trace of an AI operation as a commit graph or raw JSON.\n props:\n chat: Chat\n state:\n debug: boolean\n actions:\n toggleDebug()\n layout: |\n ┌─────────────────────────┐\n │ trace / span tree │\n └─────────────────────────┘\n```\n\n```mdl\ncomponent AssistantSettings\n desc: Settings panel for configuring the AI model provider, model defaults, and chat view.\n props:\n settings: Settings\n emits:\n onChange(settings: Settings)\n```\n\n## Operations\n\n```mdl\nop CreateChat\n key: org.dxos.operation.assistant.createChat\n desc: Creates a new Chat object and optionally adds it to the active space.\n input:\n db: Database\n name?: string\n addToSpace?: boolean\n output: Chat\n effects: [echo:write]\n```\n\n```mdl\nop UpdateChatName\n key: org.dxos.operation.assistant.updateChatName\n desc: Automatically derives and updates the name of a chat using the AI service.\n input:\n chat: Chat\n output: void\n effects: [echo:write, ai:infer]\n```\n\n```mdl\nop AnalyzeTurn\n key: org.dxos.operation.assistant.analyzeTurn\n desc: TurnReviewSkill's background end-request hook. Judges the turn that just ended with a small model and reports it when the agent struggled because of its prompting or tooling.\n input: {}\n output: void\n effects: [echo:read, ai:infer, http:post]\n```\n\n```mdl\nop SetCurrentChat\n desc: Associates a primary object with a specific companion chat in session state.\n input:\n companionTo: Obj\n chat?: Chat\n output: void\n```\n\n```mdl\nop RunPromptInNewChat\n desc: Creates a new chat, binds context objects and skills, then runs a prompt.\n note: |\n Operation DEFINED in plugin-routine (RoutineOperation.RunPromptInNewChat)\n so Routine UI and downstream plugins can dispatch it without depending on\n plugin-assistant. The HANDLER (chat creation + AiContext binding) is contributed\n here; see operations/run-prompt-in-new-chat.ts.\n```\n\n```mdl\nop EnsureCompanionChat\n key: org.dxos.operation.assistant.ensureCompanionChat\n desc: Returns the companion chat for an object, creating one if none exists.\n input:\n db: Database\n companionTo: Obj\n output:\n chat: Chat\n persisted: boolean\n effects: [echo:write]\n```\n\n```mdl\nop OnCreateSpace\n desc: Provisions initial assistant objects when a new space is created.\n input:\n space: Space\n rootCollection: Collection\n output: void\n effects: [echo:write]\n```\n\n```mdl\nop ToggleTracePanelDebug\n key: org.dxos.operation.assistant.setTracePanelDebug\n desc: Toggles the trace panel between commit-graph and raw JSON debug view.\n input:\n state?: boolean\n output: boolean\n```\n\n## Features\n\n```mdl\nfeat F-1: AI Chat\n\n req F-1.1: User can create a new Chat object in any space.\n req F-1.2: User can type a message and receive a streamed AI response.\n req F-1.3: Chat history is persisted to ECHO and replicated to all peers.\n req F-1.4:\n when: chat has enough messages\n then: chat name is automatically derived from the conversation\n```\n\n```mdl\nfeat F-2: Companion Chat\n\n req F-2.1:\n when: user opens an object that supports companions\n then: a companion chat panel is shown alongside the object\n req F-2.2:\n when: companion chat does not yet exist\n then: op:EnsureCompanionChat creates one and optionally persists it\n req F-2.3:\n when: op:EnsureCompanionChat resolves a companion chat\n then: op:BindChatContext runs every cap:SubjectContext contribution that applies to the primary\n object and binds the merged result — by default the object itself plus the skills its type\n declares via Skill.SkillsAnnotation\n```\n\n```mdl\nfeat F-3: Skill System\n\n req F-3.1: Skills define named AI configurations (instructions, tools, model).\n req F-3.2: Built-in skills include: Assistant, Browser, Database, WebSearch, Discord, Linear, Agent, Planning, Memory, Automation, SkillManager.\n req F-3.3:\n when: user selects a skill for a chat\n then: chat uses that skill's instructions and tool set\n req F-3.4: op:RunPromptInNewChat accepts skill keys to bind before sending the prompt.\n```\n\n```mdl\nfeat F-4: Multi-Provider LLM\n\n req F-4.1: Supported providers: DXOS Edge (remote), Ollama (local), LM Studio (local).\n req F-4.2: User can configure the active provider and per-provider default model in Settings.\n req F-4.3:\n when: the active provider is unavailable\n then: AiService falls back to the next registered resolver\n```\n\n```mdl\nfeat F-5: Tool Execution\n\n req F-5.1: The assistant skill exposes LayoutOperation.Open and NavigationOperation.ResolveNavigationTargets\n as tools. Both are owned elsewhere — the assistant contributes no navigation operation of its\n own, so an agent resolving an object to a path uses the same entry point the UI does.\n req F-5.2: Web search tool is registered via WebSearchToolkit.\n req F-5.3:\n when: AI invokes a tool during a conversation\n then: operation is dispatched via the compute runtime and result returned to the model\n req F-5.4: Code mode is opt-in via `Settings.codeMode`, off by default; the host supplies the engine\n through the `codeModeTurnProducer` plugin option, and without it the setting has no effect.\n req F-5.5:\n when: Settings.codeMode is enabled, the host supplied a code-mode producer, and no AgentTurnProducer is contributed\n then: agent processes spawned afterwards run turns through the code-mode producer (one `eval` tool)\n instead of AiSession\n```\n\n```mdl\nfeat F-6: ECHO Integration\n\n req F-6.1: Chat, Skill, Feed, Message, Memory, Plan, Routine, Agent, McpServer, and Sequence schemas are registered with ECHO. A chat's subject is recorded by Chat.CompanionChatAnnotation on that subject, not by a type.\n req F-6.2:\n when: a new space is created\n then: op:OnCreateSpace provisions default assistant objects\n req F-6.3: AiContext.Binding schema supports binding ECHO objects as named context variables in a conversation.\n```\n\n```mdl\nfeat F-7: Execution Trace\n\n req F-7.1: Every AI operation produces a structured span tree captured in the TracePanel.\n req F-7.2: User can toggle between commit-graph and raw JSON views via op:ToggleTracePanelDebug.\n```\n\n```mdl\nfeat F-8: Agent Struggle Reports\n\n req F-8.1: Reporting is opt-in via `Settings.reportStruggles`, off by default, and also requires telemetry to be enabled.\n req F-8.2:\n when: the chat processor issues a request with reporting enabled\n then: TurnReviewSkill is bound to the chat; its end-request hook (op:AnalyzeTurn) runs in the background, so\n the request settles without waiting for the review and the agent process awaits it before finishing\n req F-8.3:\n when: the reviewer judges the agent struggled from missing/wrong instructions or a missing/faulty tool\n then: the trajectory is uploaded to the feedback-logs bucket under `trajectories/` and an `agent_struggle` event is captured\n carrying the cause, model DXN, code mode, and enabled skills\n req F-8.4: A review that fails is logged and dropped; it never surfaces to the user.\n```\n\n## Acceptance\n\n```mdl\nscenario T-1: Create chat and send message\n given: a space is open and the assistant plugin is active\n when: user creates a new Chat and sends \"Hello\"\n then:\n - Chat object appears in the space\n - AI response is appended to Chat.messages\n - Chat is persisted to ECHO\n```\n\n```mdl\nscenario T-2: Companion chat provisioning\n given: user opens a document with no companion chat\n when: companion panel initializes\n then:\n - op:EnsureCompanionChat creates a new Chat\n - persisted === false until user sends a message\n```\n\n```mdl\nscenario T-3: Skill binding\n given: a chat exists\n when: user binds the \"WebSearch\" skill key\n then:\n - skill instructions are applied to the next AI request\n - web search tool is available in the tool list\n```\n\n```mdl\nscenario T-4: Model provider switch\n given: assistant settings are open\n when: user switches provider from edge to ollama\n then:\n - Settings.modelProvider === ollama\n - next AI request is routed to the Ollama resolver\n```\n\n```mdl\nscenario T-5: Run prompt in background\n given: a database and a prompt string are available\n when: op:RunPromptInNewChat is called with background: true\n then:\n - a new Chat is created and returned\n - the prompt runs via compute runtime without opening the UI\n```\n\n```mdl\nscenario T-7: Trace panel debug toggle\n given: trace panel is showing commit-graph view\n when: op:ToggleTracePanelDebug is invoked with no argument\n then:\n - tracePanelDebug setting flips to true\n - trace panel switches to raw JSON view\n```\n\n## QA\n\nExecutable tests. Each step carries a human instruction (`do`) and, where the step maps to an\ninvocable operation, an agent binding (`invoke`). Report failures by test and step number.\n\n```mdl\ntest QA-1: Create a chat and send a message\n desc: The core assistant round-trip — create a Chat, send a prompt, get a streamed AI response.\n actors: both\n covers: [F-1.1, F-1.2, F-1.3, F-1.4, T-1]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - no Chat named \"QA Chat\" exists — ABORT rather than deleting one this test did not create\n steps:\n - name: Create the chat\n do: Open the assistant panel and click \"New chat\".\n invoke: [op:CreateChat] { name: \"QA Chat\" }\n capture: created\n expect: a new Chat named \"QA Chat\" opens, empty\n assert: return !!$created.object\n note: |\n CreateChat returns the chat UNFILED — the handler adds its feed regardless, but persisting\n it in the space is a separate step (see plugin-space's addObject), which the UI's \"New\n chat\" gesture does implicitly.\n - name: Send a message\n do: Type \"Hello\" into the chat input and press Enter.\n expect: \"Hello\" appears as a user message, followed by a streamed assistant response, and\n the chat is persisted to ECHO and replicated to peers\n note: |\n NO `invoke` — sending a prompt runs through the AI streaming path (`AiSession`), not\n through the operation invoker; there is no `SendMessage` operation to call directly.\n - name: Chat is auto-named\n do: (no gesture — happens automatically once the chat has enough messages)\n invoke: [op:UpdateChatName] { chat: $created.object }\n expect: the chat's name changes from \"QA Chat\" to something derived from the conversation\n assert: return typeof $result === 'undefined' || true;\n note: |\n `UpdateChatName` returns void — verify the effect on `chat.name`, not the return value\n (Execution Rule 3). Included here for completeness; a fast QA pass may treat this as\n `given` already established rather than re-triggering it.\n after:\n - name: Delete the chat\n do: Right-click \"QA Chat\" in the navtree and choose Delete.\n invoke: org.dxos.operation.space.removeObjects { objects: [$created.object], target: $given.space.db }\n expect: the row leaves the navtree and its plank closes if it was open\n```\n\n```mdl\ntest QA-2: Companion chat is provisioned and bound\n desc: op:EnsureCompanionChat + op:BindChatContext — the chat a companioned object gets automatically.\n actors: both\n covers: [F-2.1, F-2.2, F-2.3, T-2]\n status: unverified\n given:\n - an identity exists\n - space: a space is open\n - subject: a Document in the space that has no companion chat yet\n steps:\n - name: Ensure the companion chat\n do: Open the document; the companion chat panel initializes automatically.\n invoke: [op:EnsureCompanionChat] { companionTo: $given.subject }\n capture: ensured\n expect: a new Chat is returned and NOT yet persisted (`persisted === false`)\n assert: return $result.persisted === false && !!$result.chat;\n - name: Bind chat context\n do: (no separate gesture — panel initialization runs this immediately after ensuring the chat)\n invoke: org.dxos.operation.assistant.bindChatContext { chat: $ensured.chat, subject: $given.subject }\n note: No `op BindChatContext` block is declared above — referenced by its full runtime key.\n expect: the companion chat's context now includes the document (and any skills its type\n declares via Skill.SkillsAnnotation)\n after:\n - name: (nothing to undo)\n do: The companion chat is never persisted unless a message is sent, so nothing was written\n to the space.\n expect: the document still has no persisted companion chat\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap\n literals?: UnionList\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self\n```\n\n```mdl\next scenario\n uri: org.dxos.mdl.scenario@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap\n state?: FieldMap\n slots?: FieldMap\n actions?: ActionMap\n emits?: EventMap\n layout?: CodeBlock\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap\n output?: TypeExpr\n errors?: ErrorMap\n effects?: EffectList\n requires?: ServiceList\n note?: Prose\n```\n";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { PLUGIN_default as t };
|
|
5
5
|
|
|
@@ -5,8 +5,8 @@ import { useCallback, useState } from "react";
|
|
|
5
5
|
import * as Hooks from "@dxos/app-framework/Hooks";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
8
|
-
import * as Button from "@dxos/react-ui/Button";
|
|
9
8
|
import * as Layout from "@dxos/react-ui/Layout";
|
|
9
|
+
import * as Button from "@dxos/react-ui/Button";
|
|
10
10
|
import * as Icon from "@dxos/react-ui/Icon";
|
|
11
11
|
import * as PluginManagerProvider from "@dxos/app-framework/PluginManagerProvider";
|
|
12
12
|
import * as Operations from "@dxos/plugin-registry/Operations";
|
|
@@ -10,9 +10,9 @@ import { log } from "@dxos/log";
|
|
|
10
10
|
import { ActionMenu } from "@dxos/react-ui-menu";
|
|
11
11
|
import { TaskQuestion } from "@dxos/react-ui-task";
|
|
12
12
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
13
|
+
import * as Layout from "@dxos/react-ui/Layout";
|
|
13
14
|
import { Task } from "@dxos/types";
|
|
14
15
|
import * as Button from "@dxos/react-ui/Button";
|
|
15
|
-
import * as Layout from "@dxos/react-ui/Layout";
|
|
16
16
|
import * as Icon from "@dxos/react-ui/Icon";
|
|
17
17
|
import * as ToolkitHooks from "@dxos/app-toolkit/Hooks";
|
|
18
18
|
import * as CardSlot from "@dxos/app-toolkit/CardSlot";
|
|
@@ -1,98 +1,154 @@
|
|
|
1
1
|
import { t as getChatPath } from "./chunk-paths.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { n as contributesPluginManager, t as bindChatDefaults } from "./chunk-default-skills.mjs";
|
|
3
|
+
import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
|
|
4
|
+
import { AssistantCapabilities } from "#types";
|
|
3
5
|
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
4
6
|
import { meta } from "#meta";
|
|
5
|
-
import { useEffect, useMemo, useState } from "react";
|
|
7
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
6
8
|
import * as Hooks from "@dxos/app-framework/Hooks";
|
|
7
9
|
import * as LayoutOperation from "@dxos/app-toolkit/LayoutOperation";
|
|
10
|
+
import * as Chat$1 from "@dxos/assistant/Chat";
|
|
11
|
+
import { Database, Feed, Ref } from "@dxos/echo";
|
|
8
12
|
import { jsx } from "react/jsx-runtime";
|
|
9
|
-
import {
|
|
13
|
+
import { usePresets } from "#hooks";
|
|
14
|
+
import { AiContext } from "@dxos/assistant";
|
|
10
15
|
import { Event } from "@dxos/async";
|
|
16
|
+
import { log } from "@dxos/log";
|
|
11
17
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
18
|
+
import * as Effect from "effect/Effect";
|
|
19
|
+
import * as EffectEx from "@dxos/effect/EffectEx";
|
|
12
20
|
import { ChatPrompt } from "#components";
|
|
13
21
|
import { useRegistry } from "@dxos/react-client/echo";
|
|
14
22
|
//#region src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx
|
|
15
|
-
|
|
16
|
-
* Home article pinned-bottom contributor: the assistant prompt. Backed by an ephemeral in-memory
|
|
17
|
-
* chat whose sole responsibility is to collect the user's text, context bindings, and preset
|
|
18
|
-
* choice, then on submit: persist the chat to the space, queue the text as a pending prompt, and
|
|
19
|
-
* navigate to it. AI generation runs in the opened chat view — the processor here exists only to
|
|
20
|
-
* back the context-binder UI.
|
|
21
|
-
*/
|
|
23
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-assistant/src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx";
|
|
22
24
|
var SpaceHomePrompt = ({ space }) => {
|
|
23
25
|
const { t } = UiHooks.useTranslation(meta.profile.key);
|
|
24
|
-
const { invokePromise } = Hooks.useOperationInvoker();
|
|
25
26
|
const registry = useRegistry();
|
|
26
27
|
const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);
|
|
27
|
-
const stateAtom = Hooks.useCapability(AssistantCapabilities.State);
|
|
28
|
-
const runtime = useChatServices({ id: space?.id });
|
|
29
28
|
const settings = Hooks.useAtomCapability(AssistantCapabilities.Settings);
|
|
30
|
-
const
|
|
31
|
-
const [
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
let cancelled = false;
|
|
39
|
-
invokePromise(AssistantOperation.CreateChat, {}, { spaceId: space.db.spaceId }).then((result) => {
|
|
40
|
-
if (!cancelled) setChat(result.data?.object);
|
|
41
|
-
});
|
|
42
|
-
return () => {
|
|
43
|
-
cancelled = true;
|
|
29
|
+
const pluginManager = contributesPluginManager(Hooks.useCapabilities(AppCapabilities.SkillDefinition));
|
|
30
|
+
const [draftGeneration, setDraftGeneration] = useState(0);
|
|
31
|
+
const draft = useMemo(() => {
|
|
32
|
+
if (!space) return;
|
|
33
|
+
const feed = Feed.make();
|
|
34
|
+
return {
|
|
35
|
+
feed,
|
|
36
|
+
chat: Chat$1.make({ feed: Ref.make(feed) })
|
|
44
37
|
};
|
|
45
|
-
}, [
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
invokePromise
|
|
49
|
-
]);
|
|
50
|
-
const processor = useChatProcessor({
|
|
38
|
+
}, [space, draftGeneration]);
|
|
39
|
+
const chat = draft?.chat;
|
|
40
|
+
const context = useDraftContext({
|
|
51
41
|
db: space?.db,
|
|
42
|
+
draft,
|
|
43
|
+
registry: atomRegistry,
|
|
44
|
+
pluginManager
|
|
45
|
+
});
|
|
46
|
+
const { preset, ...presetProps } = usePresets(settings, chat);
|
|
47
|
+
const event = useDraftSend({
|
|
48
|
+
space,
|
|
49
|
+
draft,
|
|
50
|
+
context,
|
|
51
|
+
onSent: useCallback(() => setDraftGeneration((current) => current + 1), [])
|
|
52
|
+
});
|
|
53
|
+
if (!space) return null;
|
|
54
|
+
return /* @__PURE__ */ jsx(ChatPrompt, {
|
|
55
|
+
...presetProps,
|
|
56
|
+
outline: true,
|
|
52
57
|
chat,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
registry
|
|
58
|
+
db: space.db,
|
|
59
|
+
context,
|
|
60
|
+
registry,
|
|
61
|
+
event,
|
|
62
|
+
preset: preset?.id,
|
|
63
|
+
placeholder: t("space-home.prompt.placeholder")
|
|
56
64
|
});
|
|
65
|
+
};
|
|
66
|
+
SpaceHomePrompt.displayName = "SpaceHomePrompt";
|
|
67
|
+
/** Stores the draft's chat and bindings, then opens it; a failed send removes what it stored and restores the text. */
|
|
68
|
+
var useDraftSend = ({ space, draft, context, onSent }) => {
|
|
69
|
+
const { invokePromise } = Hooks.useOperationInvoker();
|
|
70
|
+
const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);
|
|
71
|
+
const stateAtom = Hooks.useCapability(AssistantCapabilities.State);
|
|
57
72
|
const event = useMemo(() => new Event(), []);
|
|
58
73
|
useEffect(() => {
|
|
59
74
|
return event.on((ev) => {
|
|
60
75
|
if (ev.type !== "submit") return;
|
|
61
76
|
const text = ev.text.trim();
|
|
62
|
-
if (!space || !
|
|
77
|
+
if (!space || !draft || !context || text.length === 0) return;
|
|
78
|
+
const { feed, chat } = draft;
|
|
79
|
+
space.db.add(feed);
|
|
63
80
|
space.db.add(chat);
|
|
64
81
|
const chatPath = getChatPath(space.db.spaceId, chat.id);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
...current
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
82
|
+
onSent();
|
|
83
|
+
context.flush().then(() => {
|
|
84
|
+
atomRegistry.update(stateAtom, (current) => ({
|
|
85
|
+
...current,
|
|
86
|
+
pendingPrompts: {
|
|
87
|
+
...current.pendingPrompts,
|
|
88
|
+
[chatPath]: text
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
invokePromise(LayoutOperation.Open, { subject: [chatPath] });
|
|
92
|
+
}).catch((err) => {
|
|
93
|
+
log.catch(err, void 0, {
|
|
94
|
+
"~LogMeta": "~LogMeta",
|
|
95
|
+
F: __dxlog_file,
|
|
96
|
+
L: 116,
|
|
97
|
+
S: void 0
|
|
98
|
+
});
|
|
99
|
+
space.db.remove(chat);
|
|
100
|
+
space.db.remove(feed);
|
|
101
|
+
event.emit({
|
|
102
|
+
type: "update-prompt",
|
|
103
|
+
text
|
|
104
|
+
});
|
|
105
|
+
});
|
|
74
106
|
});
|
|
75
107
|
}, [
|
|
76
108
|
event,
|
|
77
109
|
space,
|
|
78
|
-
|
|
110
|
+
draft,
|
|
111
|
+
context,
|
|
79
112
|
atomRegistry,
|
|
80
113
|
stateAtom,
|
|
81
|
-
invokePromise
|
|
114
|
+
invokePromise,
|
|
115
|
+
onSent
|
|
82
116
|
]);
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
db
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
117
|
+
return event;
|
|
118
|
+
};
|
|
119
|
+
var useDraftContext = ({ db, draft, registry, pluginManager }) => {
|
|
120
|
+
const [context, setContext] = useState();
|
|
121
|
+
UiHooks.useAsyncEffect(async (controller) => {
|
|
122
|
+
if (!db || !draft) return;
|
|
123
|
+
const runtime = await EffectEx.runAndForwardErrors(Effect.context().pipe(Effect.provide(Database.layer(db))));
|
|
124
|
+
const binder = new AiContext.Binder({
|
|
125
|
+
feed: draft.feed,
|
|
126
|
+
runtime,
|
|
127
|
+
registry,
|
|
128
|
+
hold: true
|
|
129
|
+
});
|
|
130
|
+
await binder.open();
|
|
131
|
+
await bindChatDefaults(binder, {
|
|
132
|
+
chat: draft.chat,
|
|
133
|
+
pluginManager
|
|
134
|
+
});
|
|
135
|
+
if (controller.signal.aborted) {
|
|
136
|
+
binder.close();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
setContext(binder);
|
|
140
|
+
return () => {
|
|
141
|
+
setContext(void 0);
|
|
142
|
+
binder.close();
|
|
143
|
+
};
|
|
144
|
+
}, [
|
|
145
|
+
db,
|
|
146
|
+
draft,
|
|
147
|
+
registry,
|
|
148
|
+
pluginManager
|
|
149
|
+
]);
|
|
150
|
+
return context;
|
|
94
151
|
};
|
|
95
|
-
SpaceHomePrompt.displayName = "SpaceHomePrompt";
|
|
96
152
|
//#endregion
|
|
97
153
|
export { SpaceHomePrompt as default };
|
|
98
154
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-SpaceHomePrompt.mjs","names":[],"sources":["../../src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React, { useEffect, useMemo, useState } from 'react';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport
|
|
1
|
+
{"version":3,"file":"chunk-SpaceHomePrompt.mjs","names":[],"sources":["../../src/containers/SpaceHomePrompt/SpaceHomePrompt.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as LayoutOperation from '@dxos/app-toolkit/LayoutOperation';\nimport { AiContext } from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport { Event } from '@dxos/async';\nimport { Database, Feed, Ref } from '@dxos/echo';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { log } from '@dxos/log';\nimport { type Space, useRegistry } from '@dxos/react-client/echo';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { type ChatEvent, ChatPrompt } from '#components';\nimport { usePresets } from '#hooks';\nimport { meta } from '#meta';\nimport { AssistantCapabilities } from '#types';\n\nimport { getChatPath } from '../../paths.ts';\nimport { bindChatDefaults, contributesPluginManager } from '../../util/default-skills.ts';\n\ntype SpaceScopedProps = {\n space?: Space;\n};\n\nexport const SpaceHomePrompt = ({ space }: SpaceScopedProps) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n\n const registry = useRegistry();\n const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);\n const settings = Hooks.useAtomCapability(AssistantCapabilities.Settings);\n const pluginManager = contributesPluginManager(Hooks.useCapabilities(AppCapabilities.SkillDefinition));\n\n const [draftGeneration, setDraftGeneration] = useState(0);\n const draft = useMemo(() => {\n if (!space) {\n return undefined;\n }\n const feed = Feed.make();\n return { feed, chat: Chat.make({ feed: Ref.make(feed) }) };\n }, [space, draftGeneration]);\n const chat = draft?.chat;\n const context = useDraftContext({ db: space?.db, draft, registry: atomRegistry, pluginManager });\n const { preset, ...presetProps } = usePresets(settings, chat);\n\n const startNewDraft = useCallback(() => setDraftGeneration((current) => current + 1), []);\n const event = useDraftSend({ space, draft, context, onSent: startNewDraft });\n\n if (!space) {\n return null;\n }\n\n return (\n <ChatPrompt\n {...presetProps}\n outline\n chat={chat}\n db={space.db}\n context={context}\n registry={registry}\n event={event}\n preset={preset?.id}\n placeholder={t('space-home.prompt.placeholder')}\n />\n );\n};\n\nSpaceHomePrompt.displayName = 'SpaceHomePrompt';\n\ntype UseDraftSendProps = {\n space?: Space;\n draft?: { feed: Feed.Feed; chat: Chat.Chat };\n context?: AiContext.Binder;\n onSent: () => void;\n};\n\n/** Stores the draft's chat and bindings, then opens it; a failed send removes what it stored and restores the text. */\nconst useDraftSend = ({ space, draft, context, onSent }: UseDraftSendProps) => {\n const { invokePromise } = Hooks.useOperationInvoker();\n const atomRegistry = Hooks.useCapability(Capabilities.AtomRegistry);\n const stateAtom = Hooks.useCapability(AssistantCapabilities.State);\n const event = useMemo(() => new Event<ChatEvent>(), []);\n useEffect(() => {\n return event.on((ev) => {\n if (ev.type !== 'submit') {\n return;\n }\n const text = ev.text.trim();\n if (!space || !draft || !context || text.length === 0) {\n return;\n }\n\n const { feed, chat } = draft;\n space.db.add(feed);\n space.db.add(chat);\n const chatPath = getChatPath(space.db.spaceId, chat.id);\n onSent();\n void context\n .flush()\n .then(() => {\n atomRegistry.update(stateAtom, (current) => ({\n ...current,\n pendingPrompts: { ...current.pendingPrompts, [chatPath]: text },\n }));\n void invokePromise(LayoutOperation.Open, { subject: [chatPath] });\n })\n .catch((err) => {\n log.catch(err);\n space.db.remove(chat);\n space.db.remove(feed);\n event.emit({ type: 'update-prompt', text });\n });\n });\n }, [event, space, draft, context, atomRegistry, stateAtom, invokePromise, onSent]);\n return event;\n};\n\ntype UseDraftContextProps = {\n db?: Database.Database;\n draft?: { feed: Feed.Feed; chat: Chat.Chat };\n registry: AtomRegistry.AtomRegistry;\n pluginManager: boolean;\n};\n\nconst useDraftContext = ({ db, draft, registry, pluginManager }: UseDraftContextProps) => {\n const [context, setContext] = useState<AiContext.Binder>();\n UiHooks.useAsyncEffect(\n async (controller) => {\n if (!db || !draft) {\n return;\n }\n const runtime = await EffectEx.runAndForwardErrors(\n Effect.context<Database.Service>().pipe(Effect.provide(Database.layer(db))),\n );\n const binder = new AiContext.Binder({ feed: draft.feed, runtime, registry, hold: true });\n await binder.open();\n await bindChatDefaults(binder, { chat: draft.chat, pluginManager });\n if (controller.signal.aborted) {\n void binder.close();\n return;\n }\n setContext(binder);\n return () => {\n setContext(undefined);\n void binder.close();\n };\n },\n [db, draft, registry, pluginManager],\n );\n return context;\n};\n"],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import { meta } from "#meta";
|
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
import * as Hooks from "@dxos/react-ui/Hooks";
|
|
5
|
-
import * as Button from "@dxos/react-ui/Button";
|
|
6
5
|
import * as Layout from "@dxos/react-ui/Layout";
|
|
6
|
+
import * as Button from "@dxos/react-ui/Button";
|
|
7
7
|
import * as Popover from "@dxos/react-ui/Popover";
|
|
8
8
|
import * as RoutineHooks from "@dxos/plugin-routine/Hooks";
|
|
9
9
|
import * as StatusBar from "@dxos/plugin-status-bar/StatusBar";
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { n as resolveTurnProducer } from "./chunk-turn-producers.mjs";
|
|
2
2
|
import * as Capability from "@dxos/app-framework/Capability";
|
|
3
3
|
import * as RoutineCapabilities from "@dxos/plugin-routine/RoutineCapabilities";
|
|
4
|
+
import { AssistantCapabilities } from "#types";
|
|
4
5
|
import * as Capabilities from "@dxos/app-framework/Capabilities";
|
|
5
6
|
import * as Process from "@dxos/compute/Process";
|
|
6
7
|
import * as Effect from "effect/Effect";
|
|
@@ -19,7 +20,8 @@ var makeAgentServiceSpec = (codeModeTurnProducer) => LayerSpec.make({
|
|
|
19
20
|
const codeMode = codeModeTurnProducer?.(manager);
|
|
20
21
|
return AgentService.layer({
|
|
21
22
|
delegationStrategy: strategies[0],
|
|
22
|
-
makeTurnProducer: (options) => resolveTurnProducer(manager, codeMode)(options)
|
|
23
|
+
makeTurnProducer: (options) => resolveTurnProducer(manager, codeMode)(options),
|
|
24
|
+
processes: () => manager.getAll(AssistantCapabilities.AgentProcess)
|
|
23
25
|
});
|
|
24
26
|
})));
|
|
25
27
|
var agent_service_default = Capability.makeModule((options) => Effect.succeed(Capability.contribute(Capabilities.LayerSpec, makeAgentServiceSpec(options?.codeModeTurnProducer))));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-agent-service.mjs","names":[],"sources":["../../src/capabilities/agent-service.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { AgentService as AgentServiceRuntime } from '@dxos/agent-runtime';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as LayerSpec from '@dxos/compute/LayerSpec';\nimport * as Process from '@dxos/compute/Process';\nimport * as RoutineCapabilities from '@dxos/plugin-routine/RoutineCapabilities';\n\nimport { AssistantOptions } from '#types';\n\nimport { resolveTurnProducer } from './turn-producers.ts';\n\n//\n// Capability Module\n//\n// Owns the application-affinity {@link AgentService} layer for process-backed agents.\n//\n\nconst makeAgentServiceSpec = (codeModeTurnProducer: AssistantOptions.AssistantPluginOptions['codeModeTurnProducer']) =>\n LayerSpec.make(\n {\n affinity: 'application',\n // Declared, not read optionally: a tag this spec does not require is never in its context. The\n // manager spans the local and remote runtimes; plugin-routine contributes it for every stack.\n requires: [Process.ManagerService, Capability.Service],\n provides: [AgentService.AgentService],\n },\n () =>\n Layer.unwrap(\n Effect.gen(function* () {\n // Optional supervisor behaviour, contributed by a plugin that knows the agent/plan model.\n const strategies = yield* Capability.getAll(RoutineCapabilities.AgentDelegationStrategy);\n const manager = yield* Capability.Service;\n const codeMode = codeModeTurnProducer?.(manager);\n return AgentServiceRuntime.layer({\n delegationStrategy: strategies[0],\n makeTurnProducer: (options) => resolveTurnProducer(manager, codeMode)(options),\n });\n }),\n ),\n );\n\nexport default Capability.makeModule((options: AssistantOptions.AssistantPluginOptions | void) =>\n Effect.succeed(Capability.contribute(Capabilities.LayerSpec, makeAgentServiceSpec(options?.codeModeTurnProducer))),\n);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"chunk-agent-service.mjs","names":[],"sources":["../../src/capabilities/agent-service.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Layer from 'effect/Layer';\n\nimport { AgentService as AgentServiceRuntime } from '@dxos/agent-runtime';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as LayerSpec from '@dxos/compute/LayerSpec';\nimport * as Process from '@dxos/compute/Process';\nimport * as RoutineCapabilities from '@dxos/plugin-routine/RoutineCapabilities';\n\nimport { AssistantCapabilities, AssistantOptions } from '#types';\n\nimport { resolveTurnProducer } from './turn-producers.ts';\n\n//\n// Capability Module\n//\n// Owns the application-affinity {@link AgentService} layer for process-backed agents.\n//\n\nconst makeAgentServiceSpec = (codeModeTurnProducer: AssistantOptions.AssistantPluginOptions['codeModeTurnProducer']) =>\n LayerSpec.make(\n {\n affinity: 'application',\n // Declared, not read optionally: a tag this spec does not require is never in its context. The\n // manager spans the local and remote runtimes; plugin-routine contributes it for every stack.\n requires: [Process.ManagerService, Capability.Service],\n provides: [AgentService.AgentService],\n },\n () =>\n Layer.unwrap(\n Effect.gen(function* () {\n // Optional supervisor behaviour, contributed by a plugin that knows the agent/plan model.\n const strategies = yield* Capability.getAll(RoutineCapabilities.AgentDelegationStrategy);\n const manager = yield* Capability.Service;\n const codeMode = codeModeTurnProducer?.(manager);\n return AgentServiceRuntime.layer({\n delegationStrategy: strategies[0],\n makeTurnProducer: (options) => resolveTurnProducer(manager, codeMode)(options),\n processes: () => manager.getAll(AssistantCapabilities.AgentProcess),\n });\n }),\n ),\n );\n\nexport default Capability.makeModule((options: AssistantOptions.AssistantPluginOptions | void) =>\n Effect.succeed(Capability.contribute(Capabilities.LayerSpec, makeAgentServiceSpec(options?.codeModeTurnProducer))),\n);\n"],"mappings":";;;;;;;;;;;;AAyBA,IAAM,wBAAwB,yBAC5B,UAAU,KACR;CACE,UAAU;CAGV,UAAU,CAAC,QAAQ,gBAAgB,WAAW,OAAO;CACrD,UAAU,CAAC,eAAa,YAAY;AACtC,SAEE,MAAM,OACJ,OAAO,IAAI,aAAa;CAEtB,MAAM,aAAa,OAAO,WAAW,OAAO,oBAAoB,uBAAuB;CACvF,MAAM,UAAU,OAAO,WAAW;CAClC,MAAM,WAAW,uBAAuB,OAAO;CAC/C,OAAO,aAAoB,MAAM;EAC/B,oBAAoB,WAAW;EAC/B,mBAAmB,YAAY,oBAAoB,SAAS,QAAQ,CAAC,CAAC,OAAO;EAC7E,iBAAiB,QAAQ,OAAO,sBAAsB,YAAY;CACpE,CAAC;AACH,CAAC,CACH,CACJ;AAEF,IAAA,wBAAe,WAAW,YAAY,YACpC,OAAO,QAAQ,WAAW,WAAW,aAAa,WAAW,qBAAqB,SAAS,oBAAoB,CAAC,CAAC,CACnH"}
|