@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
package/PLUGIN.mdl
CHANGED
|
@@ -49,6 +49,7 @@ type Settings
|
|
|
49
49
|
modelProvider?: ModelProvider
|
|
50
50
|
modelDefaults?: ModelDefaults
|
|
51
51
|
codeMode?: boolean
|
|
52
|
+
reportStruggles?: boolean
|
|
52
53
|
tracePanelDebug?: boolean
|
|
53
54
|
```
|
|
54
55
|
|
|
@@ -159,6 +160,15 @@ op UpdateChatName
|
|
|
159
160
|
effects: [echo:write, ai:infer]
|
|
160
161
|
```
|
|
161
162
|
|
|
163
|
+
```mdl
|
|
164
|
+
op AnalyzeTurn
|
|
165
|
+
key: org.dxos.operation.assistant.analyzeTurn
|
|
166
|
+
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.
|
|
167
|
+
input: {}
|
|
168
|
+
output: void
|
|
169
|
+
effects: [echo:read, ai:infer, http:post]
|
|
170
|
+
```
|
|
171
|
+
|
|
162
172
|
```mdl
|
|
163
173
|
op SetCurrentChat
|
|
164
174
|
desc: Associates a primary object with a specific companion chat in session state.
|
|
@@ -295,6 +305,21 @@ feat F-7: Execution Trace
|
|
|
295
305
|
req F-7.2: User can toggle between commit-graph and raw JSON views via op:ToggleTracePanelDebug.
|
|
296
306
|
```
|
|
297
307
|
|
|
308
|
+
```mdl
|
|
309
|
+
feat F-8: Agent Struggle Reports
|
|
310
|
+
|
|
311
|
+
req F-8.1: Reporting is opt-in via `Settings.reportStruggles`, off by default, and also requires telemetry to be enabled.
|
|
312
|
+
req F-8.2:
|
|
313
|
+
when: the chat processor issues a request with reporting enabled
|
|
314
|
+
then: TurnReviewSkill is bound to the chat; its end-request hook (op:AnalyzeTurn) runs in the background, so
|
|
315
|
+
the request settles without waiting for the review and the agent process awaits it before finishing
|
|
316
|
+
req F-8.3:
|
|
317
|
+
when: the reviewer judges the agent struggled from missing/wrong instructions or a missing/faulty tool
|
|
318
|
+
then: the trajectory is uploaded to the feedback-logs bucket under `trajectories/` and an `agent_struggle` event is captured
|
|
319
|
+
carrying the cause, model DXN, code mode, and enabled skills
|
|
320
|
+
req F-8.4: A review that fails is logged and dropped; it never surfaces to the user.
|
|
321
|
+
```
|
|
322
|
+
|
|
298
323
|
## Acceptance
|
|
299
324
|
|
|
300
325
|
```mdl
|
|
@@ -6,6 +6,7 @@ import * as Struct from "effect/Struct";
|
|
|
6
6
|
//#region src/types/AssistantCapabilities.ts
|
|
7
7
|
var AssistantCapabilities_exports = /* @__PURE__ */ __exportAll({
|
|
8
8
|
Agent: () => Agent,
|
|
9
|
+
AgentProcess: () => AgentProcess,
|
|
9
10
|
AgentTurnProducer: () => AgentTurnProducer,
|
|
10
11
|
CompanionChatCache: () => CompanionChatCache,
|
|
11
12
|
HomeSuggestionsCache: () => HomeSuggestionsCache,
|
|
@@ -47,8 +48,14 @@ var HomeSuggestionsCache = Capability.makeSingleton()(`${meta.profile.key}.capab
|
|
|
47
48
|
*/
|
|
48
49
|
var AgentTurnProducer = Capability.make()("org.dxos.plugin.assistant.capability.agentTurnProducer");
|
|
49
50
|
var Agent = Capability.make()(`${meta.profile.key}.capability.agent`);
|
|
51
|
+
/**
|
|
52
|
+
* A durable process a chat can run on in place of the assistant's own agent process, named by its
|
|
53
|
+
* key in `chat.session.process`. The agent service spawns it for such a chat; a chat that names
|
|
54
|
+
* none runs on the assistant's.
|
|
55
|
+
*/
|
|
56
|
+
var AgentProcess = Capability.make()(`${meta.profile.key}.capability.agentProcess`);
|
|
50
57
|
var SubjectContext = Capability.make()(`${meta.profile.key}.capability.subjectContext`);
|
|
51
58
|
//#endregion
|
|
52
|
-
export { Agent, AgentTurnProducer, CompanionChatCache, HomeSuggestionsCache, HomeSuggestionsCacheSchema, OllamaManager, Settings, State, StateSchema, SubjectContext, AssistantCapabilities_exports as t };
|
|
59
|
+
export { Agent, AgentProcess, AgentTurnProducer, CompanionChatCache, HomeSuggestionsCache, HomeSuggestionsCacheSchema, OllamaManager, Settings, State, StateSchema, SubjectContext, AssistantCapabilities_exports as t };
|
|
53
60
|
|
|
54
61
|
//# sourceMappingURL=AssistantCapabilities.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AssistantCapabilities.mjs","names":[],"sources":["../../src/types/AssistantCapabilities.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Effect from 'effect/Effect';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport type { MakeTurnProducer } from '@dxos/agent-runtime';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport type { AiContext } from '@dxos/assistant';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport { type Database, type Obj, type Ref, type Registry } from '@dxos/echo';\nimport { type Message } from '@dxos/types';\n\nimport { meta } from '#meta';\n\nimport * as Assistant from './Assistant.ts';\nimport * as Ollama from './Ollama.ts';\n\nexport const Settings = Capability.makeSingleton<Atom.Writable<Assistant.Settings>>()(\n `${meta.profile.key}.capability.settings`,\n);\n\nexport const OllamaManager = Capability.makeSingleton<Ollama.Manager>()(`${meta.profile.key}.capability.ollamaManager`);\n\nexport const StateSchema = Schema.Struct({\n /** Map of primary object dxn to current chat dxn. */\n currentChat: Schema.Record(Schema.String, Schema.UndefinedOr(Schema.String)),\n /** Map of chat object path to prompt text to auto-submit when the chat opens. */\n pendingPrompts: Schema.Record(Schema.String, Schema.UndefinedOr(Schema.String)),\n}).mapFields(Struct.map(Schema.mutableKey));\n\nexport type AssistantState = Schema.Schema.Type<typeof StateSchema>;\n\nexport const State = Capability.makeSingleton<Atom.Writable<AssistantState>>()(`${meta.profile.key}.capability.state`);\n\n/** Session-scoped cache of transient (not yet persisted) companion chats keyed by companion DXN string. */\nexport const CompanionChatCache = Capability.makeSingleton<Atom.Writable<Record<string, Obj.Unknown | undefined>>>()(\n `${meta.profile.key}.capability.companionChatCache`,\n);\n\nexport const HomeSuggestionsCacheSchema = Schema.Record(\n Schema.String,\n Schema.mutableKey(\n Schema.Struct({\n /** Epoch ms timestamp of the successful generation that produced these prompts. */\n generatedAt: Schema.Number,\n /** Non-empty, trimmed prompts from a successful generation. */\n prompts: Schema.Array(Schema.String),\n /** The recent objects the prompts were generated from; an unchanged set reuses them. */\n fingerprint: Schema.optional(Schema.String),\n }),\n ),\n);\nexport type HomeSuggestionsCache = Schema.Schema.Type<typeof HomeSuggestionsCacheSchema>;\n\n/** Per-space cache of LLM-generated home starter prompts, persisted across page reloads. */\nexport const HomeSuggestionsCache = Capability.makeSingleton<Atom.Writable<HomeSuggestionsCache>>()(\n `${meta.profile.key}.capability.homeSuggestionsCache`,\n);\n\n/**\n * Optional engine for producing a conversation turn. When contributed, the agent process runs turns\n * through it instead of DXOS's own `AiSession` — e.g. a Claude Agent SDK host — while keeping its\n * queue, alarms, redelivery, delegation and hydration.\n *\n * A registry rather than a singleton, for the same reason as `AgentDelegationStrategy`: a harness\n * has to be able to contribute one before the app's own module activates without colliding.\n */\nexport const AgentTurnProducer = Capability.make<MakeTurnProducer>()(\n 'org.dxos.plugin.assistant.capability.agentTurnProducer',\n);\n\n/**\n * Whether an agent can run on this device; one that cannot is still listed, with the reason as a\n * short lowercase phrase shown after its name.\n */\nexport type AgentAvailability = { readonly available: true } | { readonly available: false; readonly reason: string };\n\n/** A person's answer to a request block an agent added to a chat. */\nexport type AgentResponse = {\n chat: Chat.Chat;\n /** The message holding the request block. */\n message: Message.Message;\n requestId: string;\n optionId: string;\n};\n\n/**\n * An agent a chat can run on, keyed by `chat.session.harness`. Each plugin registers the agents it\n * provides; this plugin registers Composer's own loop as `composer`.\n */\nexport type Agent = {\n readonly id: string;\n readonly label: string;\n readonly icon: string;\n readonly availability: Atom.Atom<AgentAvailability>;\n readonly makeTurnProducer: MakeTurnProducer;\n /** Hands a person's answer to the agent; false when nothing is waiting on that request. */\n readonly respond?: (response: AgentResponse) => Effect.Effect<boolean, never, Database.Service>;\n};\n\nexport const Agent = Capability.make<Agent>()(`${meta.profile.key}.capability.agent`);\n\n/** Context a chat receives when it runs against a subject object. */\nexport type SubjectBindings = AiContext.BindingProps & {\n /** Applied to `chat.instructions` only when the chat has none; reaches the model via the system prompt. */\n instructions?: Ref.Ref<Instructions.Instructions>;\n};\n\n/**\n * Bindings contributed for a chat opened against a subject object — the object a companion chat is\n * attached to, or the object a chat was created for.\n *\n * Every contribution whose `appliesTo` accepts the subject runs and the results are merged, so a\n * type-specific provider adds to the default rather than replacing it. Invoked by\n * `AssistantOperation.BindChatContext`, which provides `Database.Service` for the subject's space and\n * declares `Registry.Service` for skills resolved out of the hypergraph registry.\n */\nexport type SubjectContext = {\n /** Whether this provider applies to the subject. Absent ⇒ applies to every subject. */\n appliesTo?: (subject: Obj.Unknown) => boolean;\n getBindings: (params: {\n subject: Obj.Unknown;\n chat: Chat.Chat;\n }) => Effect.Effect<SubjectBindings, Error, Database.Service | Registry.Service>;\n};\n\nexport const SubjectContext = Capability.make<SubjectContext>()(`${meta.profile.key}.capability.subjectContext`);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"AssistantCapabilities.mjs","names":[],"sources":["../../src/types/AssistantCapabilities.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\n// @import-as-namespace\n\nimport type * as Effect from 'effect/Effect';\nimport type * as Atom from 'effect/reactivity/Atom';\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport type { AgentProcessDefinition, MakeTurnProducer } from '@dxos/agent-runtime';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport type { AiContext } from '@dxos/assistant';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport { type Database, type Obj, type Ref, type Registry } from '@dxos/echo';\nimport { type Message } from '@dxos/types';\n\nimport { meta } from '#meta';\n\nimport * as Assistant from './Assistant.ts';\nimport * as Ollama from './Ollama.ts';\n\nexport const Settings = Capability.makeSingleton<Atom.Writable<Assistant.Settings>>()(\n `${meta.profile.key}.capability.settings`,\n);\n\nexport const OllamaManager = Capability.makeSingleton<Ollama.Manager>()(`${meta.profile.key}.capability.ollamaManager`);\n\nexport const StateSchema = Schema.Struct({\n /** Map of primary object dxn to current chat dxn. */\n currentChat: Schema.Record(Schema.String, Schema.UndefinedOr(Schema.String)),\n /** Map of chat object path to prompt text to auto-submit when the chat opens. */\n pendingPrompts: Schema.Record(Schema.String, Schema.UndefinedOr(Schema.String)),\n}).mapFields(Struct.map(Schema.mutableKey));\n\nexport type AssistantState = Schema.Schema.Type<typeof StateSchema>;\n\nexport const State = Capability.makeSingleton<Atom.Writable<AssistantState>>()(`${meta.profile.key}.capability.state`);\n\n/** Session-scoped cache of transient (not yet persisted) companion chats keyed by companion DXN string. */\nexport const CompanionChatCache = Capability.makeSingleton<Atom.Writable<Record<string, Obj.Unknown | undefined>>>()(\n `${meta.profile.key}.capability.companionChatCache`,\n);\n\nexport const HomeSuggestionsCacheSchema = Schema.Record(\n Schema.String,\n Schema.mutableKey(\n Schema.Struct({\n /** Epoch ms timestamp of the successful generation that produced these prompts. */\n generatedAt: Schema.Number,\n /** Non-empty, trimmed prompts from a successful generation. */\n prompts: Schema.Array(Schema.String),\n /** The recent objects the prompts were generated from; an unchanged set reuses them. */\n fingerprint: Schema.optional(Schema.String),\n }),\n ),\n);\nexport type HomeSuggestionsCache = Schema.Schema.Type<typeof HomeSuggestionsCacheSchema>;\n\n/** Per-space cache of LLM-generated home starter prompts, persisted across page reloads. */\nexport const HomeSuggestionsCache = Capability.makeSingleton<Atom.Writable<HomeSuggestionsCache>>()(\n `${meta.profile.key}.capability.homeSuggestionsCache`,\n);\n\n/**\n * Optional engine for producing a conversation turn. When contributed, the agent process runs turns\n * through it instead of DXOS's own `AiSession` — e.g. a Claude Agent SDK host — while keeping its\n * queue, alarms, redelivery, delegation and hydration.\n *\n * A registry rather than a singleton, for the same reason as `AgentDelegationStrategy`: a harness\n * has to be able to contribute one before the app's own module activates without colliding.\n */\nexport const AgentTurnProducer = Capability.make<MakeTurnProducer>()(\n 'org.dxos.plugin.assistant.capability.agentTurnProducer',\n);\n\n/**\n * Whether an agent can run on this device; one that cannot is still listed, with the reason as a\n * short lowercase phrase shown after its name.\n */\nexport type AgentAvailability = { readonly available: true } | { readonly available: false; readonly reason: string };\n\n/** A person's answer to a request block an agent added to a chat. */\nexport type AgentResponse = {\n chat: Chat.Chat;\n /** The message holding the request block. */\n message: Message.Message;\n requestId: string;\n optionId: string;\n};\n\n/**\n * An agent a chat can run on, keyed by `chat.session.harness`. Each plugin registers the agents it\n * provides; this plugin registers Composer's own loop as `composer`.\n */\nexport type Agent = {\n readonly id: string;\n readonly label: string;\n readonly icon: string;\n readonly availability: Atom.Atom<AgentAvailability>;\n readonly makeTurnProducer: MakeTurnProducer;\n /** Hands a person's answer to the agent; false when nothing is waiting on that request. */\n readonly respond?: (response: AgentResponse) => Effect.Effect<boolean, never, Database.Service>;\n};\n\nexport const Agent = Capability.make<Agent>()(`${meta.profile.key}.capability.agent`);\n\n/**\n * A durable process a chat can run on in place of the assistant's own agent process, named by its\n * key in `chat.session.process`. The agent service spawns it for such a chat; a chat that names\n * none runs on the assistant's.\n */\nexport const AgentProcess = Capability.make<AgentProcessDefinition>()(`${meta.profile.key}.capability.agentProcess`);\n\n/** Context a chat receives when it runs against a subject object. */\nexport type SubjectBindings = AiContext.BindingProps & {\n /** Applied to `chat.instructions` only when the chat has none; reaches the model via the system prompt. */\n instructions?: Ref.Ref<Instructions.Instructions>;\n};\n\n/**\n * Bindings contributed for a chat opened against a subject object — the object a companion chat is\n * attached to, or the object a chat was created for.\n *\n * Every contribution whose `appliesTo` accepts the subject runs and the results are merged, so a\n * type-specific provider adds to the default rather than replacing it. Invoked by\n * `AssistantOperation.BindChatContext`, which provides `Database.Service` for the subject's space and\n * declares `Registry.Service` for skills resolved out of the hypergraph registry.\n */\nexport type SubjectContext = {\n /** Whether this provider applies to the subject. Absent ⇒ applies to every subject. */\n appliesTo?: (subject: Obj.Unknown) => boolean;\n getBindings: (params: {\n subject: Obj.Unknown;\n chat: Chat.Chat;\n }) => Effect.Effect<SubjectBindings, Error, Database.Service | Registry.Service>;\n};\n\nexport const SubjectContext = Capability.make<SubjectContext>()(`${meta.profile.key}.capability.subjectContext`);\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBA,IAAa,WAAW,WAAW,cAAiD,CAAC,CACnF,GAAG,KAAK,QAAQ,IAAI,qBACtB;AAEA,IAAa,gBAAgB,WAAW,cAA8B,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,0BAA0B;AAEtH,IAAa,cAAc,OAAO,OAAO;;CAEvC,aAAa,OAAO,OAAO,OAAO,QAAQ,OAAO,YAAY,OAAO,MAAM,CAAC;;CAE3E,gBAAgB,OAAO,OAAO,OAAO,QAAQ,OAAO,YAAY,OAAO,MAAM,CAAC;AAChF,CAAC,CAAC,CAAC,UAAU,OAAO,IAAI,OAAO,UAAU,CAAC;AAI1C,IAAa,QAAQ,WAAW,cAA6C,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,kBAAkB;;AAGrH,IAAa,qBAAqB,WAAW,cAAsE,CAAC,CAClH,GAAG,KAAK,QAAQ,IAAI,+BACtB;AAEA,IAAa,6BAA6B,OAAO,OAC/C,OAAO,QACP,OAAO,WACL,OAAO,OAAO;;CAEZ,aAAa,OAAO;;CAEpB,SAAS,OAAO,MAAM,OAAO,MAAM;;CAEnC,aAAa,OAAO,SAAS,OAAO,MAAM;AAC5C,CAAC,CACH,CACF;;AAIA,IAAa,uBAAuB,WAAW,cAAmD,CAAC,CACjG,GAAG,KAAK,QAAQ,IAAI,iCACtB;;;;;;;;;AAUA,IAAa,oBAAoB,WAAW,KAAuB,CAAC,CAClE,wDACF;AA+BA,IAAa,QAAQ,WAAW,KAAY,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,kBAAkB;;;;;;AAOpF,IAAa,eAAe,WAAW,KAA6B,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,yBAAyB;AA0BnH,IAAa,iBAAiB,WAAW,KAAqB,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,2BAA2B"}
|
package/dist/lib/Settings.mjs
CHANGED
|
@@ -52,6 +52,10 @@ var Settings = Schema.Struct({
|
|
|
52
52
|
title: "Default agent",
|
|
53
53
|
description: "Agent that one-click \"Assign to agent\" hands tasks to, by id: composer, or another installed agent such as claude-code. Falls back to Composer where that agent is unavailable."
|
|
54
54
|
})),
|
|
55
|
+
reportStruggles: Schema.optional(Schema.Boolean.annotate({
|
|
56
|
+
title: "Report agent struggles",
|
|
57
|
+
description: "After each agent turn, a small model reviews the conversation. If the agent struggled because of its instructions or tools, the conversation (including content from your space) is sent to the DXOS team to improve Composer. Requires telemetry to be enabled."
|
|
58
|
+
})),
|
|
55
59
|
tracePanelDebug: Schema.optional(Schema.Boolean.annotate({
|
|
56
60
|
title: "Trace panel debug",
|
|
57
61
|
description: "Show the raw span tree as JSON in the trace panel instead of the commit graph."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Settings.mjs","names":[],"sources":["../../src/types/Settings.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport { Provider } from '@dxos/ai';\nimport * as SchemaEx from '@dxos/effect/SchemaEx';\nimport { DXN } from '@dxos/keys';\nimport { ChatView } from '@dxos/react-ui-assistant/types';\n\n// A provider id is an open DXN (third-party providers define their own), validated as a DXN rather\n// than restricted to a closed literal union. The known providers come from the @dxos/ai registry.\nexport const ModelProvider = DXN.Schema;\nexport type ModelProvider = DXN.DXN;\nexport const ModelProviders: readonly DXN.DXN[] = Provider.all.map((provider) => provider.id);\n\n// The view type is the thread's own concern (`@dxos/react-ui-assistant`); only the literal values\n// survive here, for the settings UI.\nexport const ChatViews = SchemaEx.getLiteralValues(ChatView);\n\nexport const ModelDefaults = Schema.Struct({\n edge: Schema.optional(\n Schema.String.annotate({\n title: 'Remote language model',\n description: 'Choose the remote language model used for AI requests.',\n }),\n ),\n // `built-in` (bundled sidecar) and `ollama` (external) share the `ollama` model source, so they\n // share this single default key.\n ollama: Schema.optional(\n Schema.String.annotate({\n title: 'Local language model',\n description: 'Choose the locally hosted model used for AI requests.',\n }),\n ),\n lmstudio: Schema.optional(\n Schema.String.annotate({\n title: 'LM Studio language model',\n description: 'Choose the locally hosted LM Studio model for AI requests.',\n }),\n ),\n}).mapFields(Struct.map(Schema.mutableKey));\nexport type ModelDefaults = Schema.Schema.Type<typeof ModelDefaults>;\n\nexport const Settings = Schema.Struct({\n customPrompts: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Use custom prompts',\n description: 'Allow the assistant to use custom prompts defined in your spaces.',\n }),\n ),\n chatView: Schema.optional(\n ChatView.annotate({\n title: 'Chat view',\n description:\n 'Controls which message blocks are shown in the chat: normal hides reasoning, thinking shows reasoning, debug shows raw blocks, summary shows only conversational text.',\n }),\n ),\n modelProvider: Schema.optional(\n ModelProvider.annotate({\n title: 'LLM provider',\n description: 'Select which language model service to use for AI responses.',\n }),\n ),\n modelDefaults: Schema.optional(ModelDefaults.annotate({ title: 'Model defaults' })),\n codeMode: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Code mode (experimental)',\n description:\n 'Run agent turns in code mode: the model writes code against the workspace instead of calling a tool per action. The code runs off the page but can read your workspace data, make network requests, and access browser storage for this origin, so enable it only for trusted content. Applies to agents started after the change.',\n }),\n ),\n defaultAgent: Schema.optional(\n Schema.String.annotate({\n title: 'Default agent',\n description:\n 'Agent that one-click \"Assign to agent\" hands tasks to, by id: composer, or another installed agent such as claude-code. Falls back to Composer where that agent is unavailable.',\n }),\n ),\n tracePanelDebug: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Trace panel debug',\n description: 'Show the raw span tree as JSON in the trace panel instead of the commit graph.',\n }),\n ),\n traceProcessEnvironments: Schema.optional(\n Schema.mutable(Schema.Array(Schema.String)).annotate({\n title: 'Trace panel process environments',\n description:\n 'Process environments (app, space, conversation) shown in the trace panel. Unset uses the default selection, which hides app-level chatter.',\n }),\n ),\n}).mapFields(Struct.map(Schema.mutableKey));\nexport interface Settings extends Schema.Schema.Type<typeof Settings> {}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAa,gBAAgB,IAAI;AAEjC,IAAa,iBAAqC,SAAS,IAAI,KAAK,aAAa,SAAS,EAAE;AAI5F,IAAa,YAAY,SAAS,iBAAiB,QAAQ;AAE3D,IAAa,gBAAgB,OAAO,OAAO;CACzC,MAAM,OAAO,SACX,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CAGA,QAAQ,OAAO,SACb,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,UAAU,OAAO,SACf,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;AACF,CAAC,CAAC,CAAC,UAAU,OAAO,IAAI,OAAO,UAAU,CAAC;AAG1C,IAAa,WAAW,OAAO,OAAO;CACpC,eAAe,OAAO,SACpB,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,UAAU,OAAO,SACf,SAAS,SAAS;EAChB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,eAAe,OAAO,SACpB,cAAc,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,eAAe,OAAO,SAAS,cAAc,SAAS,EAAE,OAAO,iBAAiB,CAAC,CAAC;CAClF,UAAU,OAAO,SACf,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,cAAc,OAAO,SACnB,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,iBAAiB,OAAO,SACtB,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,0BAA0B,OAAO,SAC/B,OAAO,QAAQ,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,CAAC,SAAS;EACnD,OAAO;EACP,aACE;CACJ,CAAC,CACH;AACF,CAAC,CAAC,CAAC,UAAU,OAAO,IAAI,OAAO,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"Settings.mjs","names":[],"sources":["../../src/types/Settings.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\nimport * as Struct from 'effect/Struct';\n\nimport { Provider } from '@dxos/ai';\nimport * as SchemaEx from '@dxos/effect/SchemaEx';\nimport { DXN } from '@dxos/keys';\nimport { ChatView } from '@dxos/react-ui-assistant/types';\n\n// A provider id is an open DXN (third-party providers define their own), validated as a DXN rather\n// than restricted to a closed literal union. The known providers come from the @dxos/ai registry.\nexport const ModelProvider = DXN.Schema;\nexport type ModelProvider = DXN.DXN;\nexport const ModelProviders: readonly DXN.DXN[] = Provider.all.map((provider) => provider.id);\n\n// The view type is the thread's own concern (`@dxos/react-ui-assistant`); only the literal values\n// survive here, for the settings UI.\nexport const ChatViews = SchemaEx.getLiteralValues(ChatView);\n\nexport const ModelDefaults = Schema.Struct({\n edge: Schema.optional(\n Schema.String.annotate({\n title: 'Remote language model',\n description: 'Choose the remote language model used for AI requests.',\n }),\n ),\n // `built-in` (bundled sidecar) and `ollama` (external) share the `ollama` model source, so they\n // share this single default key.\n ollama: Schema.optional(\n Schema.String.annotate({\n title: 'Local language model',\n description: 'Choose the locally hosted model used for AI requests.',\n }),\n ),\n lmstudio: Schema.optional(\n Schema.String.annotate({\n title: 'LM Studio language model',\n description: 'Choose the locally hosted LM Studio model for AI requests.',\n }),\n ),\n}).mapFields(Struct.map(Schema.mutableKey));\nexport type ModelDefaults = Schema.Schema.Type<typeof ModelDefaults>;\n\nexport const Settings = Schema.Struct({\n customPrompts: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Use custom prompts',\n description: 'Allow the assistant to use custom prompts defined in your spaces.',\n }),\n ),\n chatView: Schema.optional(\n ChatView.annotate({\n title: 'Chat view',\n description:\n 'Controls which message blocks are shown in the chat: normal hides reasoning, thinking shows reasoning, debug shows raw blocks, summary shows only conversational text.',\n }),\n ),\n modelProvider: Schema.optional(\n ModelProvider.annotate({\n title: 'LLM provider',\n description: 'Select which language model service to use for AI responses.',\n }),\n ),\n modelDefaults: Schema.optional(ModelDefaults.annotate({ title: 'Model defaults' })),\n codeMode: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Code mode (experimental)',\n description:\n 'Run agent turns in code mode: the model writes code against the workspace instead of calling a tool per action. The code runs off the page but can read your workspace data, make network requests, and access browser storage for this origin, so enable it only for trusted content. Applies to agents started after the change.',\n }),\n ),\n defaultAgent: Schema.optional(\n Schema.String.annotate({\n title: 'Default agent',\n description:\n 'Agent that one-click \"Assign to agent\" hands tasks to, by id: composer, or another installed agent such as claude-code. Falls back to Composer where that agent is unavailable.',\n }),\n ),\n reportStruggles: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Report agent struggles',\n description:\n 'After each agent turn, a small model reviews the conversation. If the agent struggled because of its instructions or tools, the conversation (including content from your space) is sent to the DXOS team to improve Composer. Requires telemetry to be enabled.',\n }),\n ),\n tracePanelDebug: Schema.optional(\n Schema.Boolean.annotate({\n title: 'Trace panel debug',\n description: 'Show the raw span tree as JSON in the trace panel instead of the commit graph.',\n }),\n ),\n traceProcessEnvironments: Schema.optional(\n Schema.mutable(Schema.Array(Schema.String)).annotate({\n title: 'Trace panel process environments',\n description:\n 'Process environments (app, space, conversation) shown in the trace panel. Unset uses the default selection, which hides app-level chatter.',\n }),\n ),\n}).mapFields(Struct.map(Schema.mutableKey));\nexport interface Settings extends Schema.Schema.Type<typeof Settings> {}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,IAAa,gBAAgB,IAAI;AAEjC,IAAa,iBAAqC,SAAS,IAAI,KAAK,aAAa,SAAS,EAAE;AAI5F,IAAa,YAAY,SAAS,iBAAiB,QAAQ;AAE3D,IAAa,gBAAgB,OAAO,OAAO;CACzC,MAAM,OAAO,SACX,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CAGA,QAAQ,OAAO,SACb,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,UAAU,OAAO,SACf,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;AACF,CAAC,CAAC,CAAC,UAAU,OAAO,IAAI,OAAO,UAAU,CAAC;AAG1C,IAAa,WAAW,OAAO,OAAO;CACpC,eAAe,OAAO,SACpB,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,UAAU,OAAO,SACf,SAAS,SAAS;EAChB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,eAAe,OAAO,SACpB,cAAc,SAAS;EACrB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,eAAe,OAAO,SAAS,cAAc,SAAS,EAAE,OAAO,iBAAiB,CAAC,CAAC;CAClF,UAAU,OAAO,SACf,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,cAAc,OAAO,SACnB,OAAO,OAAO,SAAS;EACrB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,iBAAiB,OAAO,SACtB,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aACE;CACJ,CAAC,CACH;CACA,iBAAiB,OAAO,SACtB,OAAO,QAAQ,SAAS;EACtB,OAAO;EACP,aAAa;CACf,CAAC,CACH;CACA,0BAA0B,OAAO,SAC/B,OAAO,QAAQ,OAAO,MAAM,OAAO,MAAM,CAAC,CAAC,CAAC,SAAS;EACnD,OAAO;EACP,aACE;CACJ,CAAC,CACH;AACF,CAAC,CAAC,CAAC,UAAU,OAAO,IAAI,OAAO,UAAU,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
|
|
2
|
+
import * as Capability from "@dxos/app-framework/Capability";
|
|
3
|
+
import { DXN, Database, Ref } from "@dxos/echo";
|
|
4
|
+
import { AiService } from "@dxos/ai";
|
|
5
|
+
import * as Operation from "@dxos/compute/Operation";
|
|
6
|
+
import * as Skill from "@dxos/compute/Skill";
|
|
7
|
+
import * as Schema from "effect/Schema";
|
|
8
|
+
import * as Harness from "@dxos/assistant/Harness";
|
|
9
|
+
//#region src/skills/turn-review/TurnReviewSkill.ts
|
|
10
|
+
var TurnReviewSkill_exports = /* @__PURE__ */ __exportAll({
|
|
11
|
+
AnalyzeTurn: () => AnalyzeTurn,
|
|
12
|
+
key: () => key,
|
|
13
|
+
make: () => make
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* The skill's end-request hook: asks a small model whether the agent struggled in the turn that just
|
|
17
|
+
* ended because of its prompting or tooling, and if so reports the trajectory. Defined beside the
|
|
18
|
+
* skill rather than in `#types` because the harness service it needs carries the session runtime,
|
|
19
|
+
* which the UI must not load.
|
|
20
|
+
*/
|
|
21
|
+
var AnalyzeTurn = Operation.make({
|
|
22
|
+
meta: {
|
|
23
|
+
key: DXN.make("org.dxos.operation.assistant.analyzeTurn"),
|
|
24
|
+
name: "Analyze Agent Turn",
|
|
25
|
+
icon: "ph--magnifying-glass--regular"
|
|
26
|
+
},
|
|
27
|
+
services: [
|
|
28
|
+
Harness.HarnessService,
|
|
29
|
+
Database.Service,
|
|
30
|
+
AiService.AiService,
|
|
31
|
+
Capability.Service
|
|
32
|
+
],
|
|
33
|
+
input: Schema.Struct({}),
|
|
34
|
+
output: Schema.Void
|
|
35
|
+
});
|
|
36
|
+
var key = "org.dxos.skill.assistant.turnReview";
|
|
37
|
+
/**
|
|
38
|
+
* Bound to a chat by the chat processor while the user has opted into struggle reports. It adds no
|
|
39
|
+
* tools or instructions; it exists only for its hook, which runs in the background so the user never
|
|
40
|
+
* waits on the review.
|
|
41
|
+
*/
|
|
42
|
+
var make = () => Skill.make({
|
|
43
|
+
key,
|
|
44
|
+
name: "Turn Review",
|
|
45
|
+
description: "Reports agent turns that struggled because of their instructions or tools (opt-in telemetry).",
|
|
46
|
+
hooks: [{
|
|
47
|
+
spec: { _tag: "end-request" },
|
|
48
|
+
async: true,
|
|
49
|
+
function: Ref.make(Operation.serialize(AnalyzeTurn))
|
|
50
|
+
}]
|
|
51
|
+
});
|
|
52
|
+
//#endregion
|
|
53
|
+
export { AnalyzeTurn, key, make, TurnReviewSkill_exports as t };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=TurnReviewSkill.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TurnReviewSkill.mjs","names":[],"sources":["../../src/skills/turn-review/TurnReviewSkill.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as Schema from 'effect/Schema';\n\nimport { AiService } from '@dxos/ai';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Harness from '@dxos/assistant/Harness';\nimport * as Operation from '@dxos/compute/Operation';\nimport * as Skill from '@dxos/compute/Skill';\nimport { Database, DXN, Ref } from '@dxos/echo';\n\n/**\n * The skill's end-request hook: asks a small model whether the agent struggled in the turn that just\n * ended because of its prompting or tooling, and if so reports the trajectory. Defined beside the\n * skill rather than in `#types` because the harness service it needs carries the session runtime,\n * which the UI must not load.\n */\nexport const AnalyzeTurn = Operation.make({\n meta: {\n key: DXN.make('org.dxos.operation.assistant.analyzeTurn'),\n name: 'Analyze Agent Turn',\n icon: 'ph--magnifying-glass--regular',\n },\n services: [Harness.HarnessService, Database.Service, AiService.AiService, Capability.Service],\n input: Schema.Struct({}),\n output: Schema.Void,\n});\n\nexport const key = 'org.dxos.skill.assistant.turnReview';\n\n/**\n * Bound to a chat by the chat processor while the user has opted into struggle reports. It adds no\n * tools or instructions; it exists only for its hook, which runs in the background so the user never\n * waits on the review.\n */\nexport const make = (): Skill.Skill =>\n Skill.make({\n key,\n name: 'Turn Review',\n description: 'Reports agent turns that struggled because of their instructions or tools (opt-in telemetry).',\n hooks: [{ spec: { _tag: 'end-request' }, async: true, function: Ref.make(Operation.serialize(AnalyzeTurn)) }],\n });\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,cAAc,UAAU,KAAK;CACxC,MAAM;EACJ,KAAK,IAAI,KAAK,0CAA0C;EACxD,MAAM;EACN,MAAM;CACR;CACA,UAAU;EAAC,QAAQ;EAAgB,SAAS;EAAS,UAAU;EAAW,WAAW;CAAO;CAC5F,OAAO,OAAO,OAAO,CAAC,CAAC;CACvB,QAAQ,OAAO;AACjB,CAAC;AAED,IAAa,MAAM;;;;;;AAOnB,IAAa,aACX,MAAM,KAAK;CACT;CACA,MAAM;CACN,aAAa;CACb,OAAO,CAAC;EAAE,MAAM,EAAE,MAAM,cAAc;EAAG,OAAO;EAAM,UAAU,IAAI,KAAK,UAAU,UAAU,WAAW,CAAC;CAAE,CAAC;AAC9G,CAAC"}
|
|
@@ -6,9 +6,9 @@ import { useObject } from "@dxos/echo-react";
|
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Agent from "@dxos/assistant/Agent";
|
|
9
10
|
import * as Button from "@dxos/react-ui/Button";
|
|
10
11
|
import * as Toolbar from "@dxos/react-ui/Toolbar";
|
|
11
|
-
import * as Agent from "@dxos/assistant/Agent";
|
|
12
12
|
import * as Panel from "@dxos/react-ui/Panel";
|
|
13
13
|
import * as InstructionsEditor from "@dxos/plugin-routine/InstructionsEditor";
|
|
14
14
|
//#region src/containers/AgentArticle/AgentArticle.tsx
|
|
@@ -7,9 +7,9 @@ import * as Hooks from "@dxos/app-framework/Hooks";
|
|
|
7
7
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
8
8
|
import { Model, Provider } from "@dxos/ai";
|
|
9
9
|
import * as UiHooks from "@dxos/react-ui/Hooks";
|
|
10
|
+
import * as Layout from "@dxos/react-ui/Layout";
|
|
10
11
|
import * as EffectEx from "@dxos/effect/EffectEx";
|
|
11
12
|
import * as Button from "@dxos/react-ui/Button";
|
|
12
|
-
import * as Layout from "@dxos/react-ui/Layout";
|
|
13
13
|
import { Form, createSelectField } from "@dxos/react-ui-form";
|
|
14
14
|
import { List, ListItem } from "@dxos/react-list";
|
|
15
15
|
import * as Combobox from "@dxos/react-ui/Combobox";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-AssistantSettings.mjs","names":[],"sources":["../../src/containers/AssistantSettings/OllamaModels.tsx","../../src/containers/AssistantSettings/AssistantSettings.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport React, { useCallback, useEffect, useState } from 'react';\n\nimport { Model, Provider } from '@dxos/ai';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { List, ListItem } from '@dxos/react-list';\nimport { Form } from '@dxos/react-ui-form';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Combobox from '@dxos/react-ui/Combobox';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\nimport * as Layout from '@dxos/react-ui/Layout';\n\nimport { meta } from '#meta';\nimport { AssistantCapabilities, Ollama } from '#types';\n\n/** Quick-pick model names (Ollama pull tags) sourced from the curated catalog. */\nconst QUICK_PICKS = Model.forProvider(Provider.ollama.id).map((model) => model.backend);\n\n/** Poll interval for the loaded-into-memory set while the settings panel is open. */\nconst LOADED_POLL_INTERVAL = 3_000;\n\n/**\n * Manage locally-installed Ollama models. The {@link AssistantCapabilities.OllamaManager}\n * capability is only contributed by the native (desktop) runtime, so this renders nothing in the\n * browser/mobile. Split from the section body so hooks are never called conditionally.\n */\nexport const OllamaModels = () => {\n const manager = Hooks.useOptionalCapability(AssistantCapabilities.OllamaManager);\n if (!manager) {\n return null;\n }\n\n return <OllamaModelsSection manager={manager} />;\n};\n\nexport const OllamaModelsSection = ({ manager }: { manager: Ollama.Manager }) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n // Explicit subscription: the list and badges must re-render as pulls/installs/loads land.\n const state = useAtomValue(manager.state);\n const [query, setQuery] = useState('');\n const [open, setOpen] = useState(false);\n // UI-level gate keyed by model name, guaranteeing a visible busy state across the click handler.\n const [pending, setPending] = useState<Record<string, boolean>>({});\n\n // Ensure the sidecar is up and list installed models on mount.\n useEffect(() => {\n void EffectEx.runPromise(manager.refresh);\n }, [manager]);\n\n // Poll the loaded-into-memory set so the panel reflects which model is resident in real time.\n useEffect(() => {\n const interval = setInterval(() => void EffectEx.runPromise(manager.refreshLoaded), LOADED_POLL_INTERVAL);\n return () => clearInterval(interval);\n }, [manager]);\n\n const installed = new Set(state.models.map((model) => model.name));\n const loaded = new Map(state.loaded.map((model) => [model.name, model]));\n const pulling = Object.keys(state.pulls).filter((name) => !installed.has(name));\n\n const withPending = useCallback(\n (key: string, action: () => Promise<void>) => async () => {\n setPending((prev) => ({ ...prev, [key]: true }));\n try {\n await action();\n } finally {\n setPending((prev) => ({ ...prev, [key]: false }));\n }\n },\n [],\n );\n\n const handlePull = useCallback(\n (target: string) => {\n const trimmed = target.trim();\n if (trimmed.length === 0) {\n return;\n }\n setQuery('');\n setOpen(false);\n void withPending(trimmed, () => EffectEx.runPromise(manager.pull(trimmed)))();\n },\n [manager, withPending],\n );\n\n // Caller-driven filtering: curated picks not yet installed, matching the typed query.\n const filter = query.trim();\n const suggestions = QUICK_PICKS.filter((pick) => !installed.has(pick) && (filter === '' || pick.includes(filter)));\n const offerCustom = filter.length > 0 && !installed.has(filter) && !suggestions.includes(filter);\n const empty = state.models.length === 0 && pulling.length === 0;\n\n return (\n <Form.FieldSet label={t('settings.ollama.title')}>\n <Form.Field standalone label={t('settings.ollama.installed.label')}>\n {state.kind === 'failed' && state.error ? (\n // Connection-level failure has no associated model, so it shows inline as the row content.\n <p className='text-sm text-error-text'>{t('settings.ollama.failed.message', { error: state.error })}</p>\n ) : empty ? (\n <p className='text-sm text-fg-muted'>{t('settings.ollama.empty.message')}</p>\n ) : (\n // Plain `List`/`ListItem` (non-select), with the trigger editor's \"fatter\" two-line row\n // treatment: a name line plus a secondary meta line, on a surface-styled row. Rows are\n // actioned via their trailing buttons (no selection semantics).\n <List variant='unordered' className='flex flex-col gap-1 grow w-full text-left'>\n {state.models.map((model) => {\n const running = loaded.get(model.name);\n const error = state.errors[model.name];\n const size = model.size != null ? formatBytes(model.size) : undefined;\n const loadedLabel = running\n ? running.sizeVram\n ? t('settings.ollama.loaded.vram', { size: formatBytes(running.sizeVram) })\n : t('settings.ollama.loaded.label')\n : undefined;\n return (\n <ListItem\n key={model.name}\n className='flex flex-col gap-trim-xs rounded-sm dx-input-surface px-trim-sm py-trim-xs w-full'\n >\n <Layout.Flex gap='sm' align='center'>\n <span className='grow truncate font-medium'>{model.name}</span>\n <Button.Root\n icon={running ? 'ph--eject--regular' : 'ph--play--regular'}\n iconOnly\n label={running ? t('settings.ollama.unload.label') : t('settings.ollama.load.label')}\n disabled={pending[model.name]}\n onClick={() =>\n void withPending(model.name, () =>\n EffectEx.runPromise(running ? manager.unload(model.name) : manager.load(model.name)),\n )()\n }\n />\n <Button.Root\n icon='ph--trash--regular'\n iconOnly\n label={t('settings.ollama.remove.label')}\n disabled={pending[model.name]}\n onClick={() =>\n void withPending(model.name, () => EffectEx.runPromise(manager.remove(model.name)))()\n }\n />\n </Layout.Flex>\n {(size || loadedLabel || error) && (\n <Layout.Flex gap='sm' align='center' classNames='text-sm'>\n {size && <span className='text-fg-muted'>{size}</span>}\n {loadedLabel && <span className='text-success-text'>{loadedLabel}</span>}\n {error && <span className='truncate text-error-text'>{shortError(error)}</span>}\n </Layout.Flex>\n )}\n </ListItem>\n );\n })}\n {pulling.map((name) => {\n const progress = state.pulls[name];\n const status = progress?.total\n ? t('settings.ollama.pulling.message', { percent: percentOf(progress) })\n : (progress?.status ?? t('settings.ollama.pulling.label'));\n return (\n <ListItem\n key={name}\n className='flex flex-col gap-trim-xs rounded-sm dx-input-surface px-trim-sm py-trim-xs w-full'\n >\n <Layout.Flex gap='sm' align='center'>\n <span className='grow truncate font-medium text-fg-muted'>{name}</span>\n <Button.Root\n icon='ph--x--regular'\n iconOnly\n label={t('settings.ollama.cancel.label')}\n onClick={() => void EffectEx.runPromise(manager.cancel(name))}\n />\n </Layout.Flex>\n <span className='text-sm text-fg-muted'>{status}</span>\n </ListItem>\n );\n })}\n </List>\n )}\n </Form.Field>\n\n <Form.Field label={t('settings.ollama.pull.label')}>\n {/* Pull failures for not-yet-installed models surface here (no model row to attach to). */}\n {Object.entries(state.errors)\n .filter(([name]) => !installed.has(name))\n .map(([name, error]) => (\n <p key={name} className='text-xs text-error-text'>\n {name}: {shortError(error)}\n </p>\n ))}\n {/* Root value is held empty so the trigger always shows the placeholder; the live text is\n the separate `query` driving the search field and suggestion filter. A query that names no\n pick is offered as the create row, which pulls it. */}\n <Combobox.Root\n items={suggestions.map((pick) => ({ value: pick, label: pick }))}\n filter={null}\n open={open}\n onOpenChange={({ open }) => setOpen(open)}\n value={[]}\n onValueChange={({ value: [pick] }) => pick && handlePull(pick)}\n inputValue={query}\n onInputValueChange={({ inputValue }) => setQuery(inputValue)}\n onCreate={offerCustom ? handlePull : undefined}\n createLabel={(name) => t('settings.ollama.pull-custom.label', { name })}\n createIcon='ph--download-simple--regular'\n >\n <Combobox.Trigger classNames='w-full' placeholder={t('settings.ollama.pull.placeholder')} />\n <Combobox.Content>\n <Combobox.Input placeholder={t('settings.ollama.pull.placeholder')} />\n <Combobox.List>\n {suggestions.map((pick) => (\n <Combobox.Item key={pick} item={{ value: pick, label: pick }} />\n ))}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n </Form.Field>\n </Form.FieldSet>\n );\n};\n\n/**\n * Condense a verbose service error for inline display: drop Ollama's \"(checked: <paths>)\" path dump\n * and cap the length. The full message is still logged to the console.\n */\nconst shortError = (error: string): string => {\n const head = error.split(/\\s*\\(checked:/)[0].trim();\n return head.length > 160 ? `${head.slice(0, 160)}…` : head;\n};\n\n/** Percent complete for an in-flight pull, or 0 when totals are not yet known. */\nconst percentOf = (progress: Ollama.ModelsState['pulls'][string] | undefined): number =>\n progress && progress.total && progress.total > 0 ? Math.round(((progress.completed ?? 0) / progress.total) * 100) : 0;\n\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];\n\n/** Human-readable byte size (binary units). */\nconst formatBytes = (bytes: number): string => {\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n return `${value.toFixed(unit === 0 ? 0 : 1)} ${UNITS[unit]}`;\n};\n\nOllamaModels.displayName = 'OllamaModels';\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport React, { useMemo } from 'react';\n\nimport { Provider } from '@dxos/ai';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { type DXN } from '@dxos/keys';\nimport { Form, type FormFieldMap, createSelectField } from '@dxos/react-ui-form';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { meta } from '#meta';\nimport { Assistant, AssistantCapabilities, Ollama } from '#types';\n\nimport { presetsForProvider, resolveProvider } from '../../processor/index.ts';\nimport { OllamaModels } from './OllamaModels.tsx';\n\nexport type AssistantSettingsProps = AppSurface.SettingsProps<Assistant.Settings>;\n\n// The per-provider model-default options must match the chat picker (usePresets): the catalog models\n// served by the provider, restricted to installed models when `installed` is supplied (local sidecar).\nconst presetOptions = (provider: DXN.DXN, installed?: ReadonlySet<string>) =>\n presetsForProvider(provider)\n .filter((preset) => !installed || installed.has(preset.backend))\n .map((preset) => ({ value: preset.model, label: preset.label }));\n\nexport const AssistantSettings = ({ settings, onSettingsChange, scope }: AssistantSettingsProps) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n\n // The Ollama manager is the bundled sidecar (desktop only). Its presence selects the local\n // provider: the managed `built-in` vs. an external `ollama` server.\n const ollamaManager = Hooks.useOptionalCapability(AssistantCapabilities.OllamaManager);\n const localProvider = ollamaManager ? Provider.builtIn : Provider.ollama;\n const localProviderKey = ollamaManager ? 'built-in' : 'ollama';\n\n // Installed models reported by the bundled sidecar; restricts the local default to present models.\n const localModels = useAtomValue(ollamaManager?.state ?? Ollama.emptyState);\n const installed = useMemo(() => new Set(localModels.models.map((model) => model.name)), [localModels]);\n\n // Reconcile a stored provider with the runtime (`ollama` → `built-in` on desktop) so the\n // dropdown shows a concrete, available selection rather than blank.\n const values = useMemo(\n () => ({ ...settings, modelProvider: resolveProvider(settings.modelProvider, !!ollamaManager) }),\n [settings, ollamaManager],\n );\n\n // Form `fieldMap` is keyed by dotted JSON path, so nested struct leaves are addressed directly.\n const fieldMap = useMemo<FormFieldMap>(\n () => ({\n // `defaultLabel: null` drops the sentinel \"Default\" option — a provider is always concrete.\n 'modelProvider': createSelectField({\n defaultLabel: null,\n options: [\n { value: Provider.edge.id, label: t('settings.provider.edge.label') },\n { value: localProvider.id, label: t(`settings.provider.${localProviderKey}.label`) },\n { value: Provider.lmStudio.id, label: t('settings.provider.lmstudio.label') },\n ],\n }),\n 'modelDefaults.edge': createSelectField({ options: presetOptions(Provider.edge.id) }),\n // `built-in` and `ollama` share the `ollama` default key; the managed sidecar restricts to installed.\n 'modelDefaults.ollama': createSelectField({\n options: presetOptions(localProvider.id, ollamaManager ? installed : undefined),\n }),\n 'modelDefaults.lmstudio': createSelectField({ options: presetOptions(Provider.lmStudio.id) }),\n }),\n [t, localProvider, localProviderKey, ollamaManager, installed],\n );\n\n return (\n <Form.Root\n variant='settings'\n schema={Assistant.Settings}\n readonly={!onSettingsChange}\n values={values}\n onValuesChanged={(values) => onSettingsChange?.(() => values)}\n >\n <Form.Viewport scroll>\n <Form.Content>\n <Form.FieldSet label={meta.profile.name ?? meta.profile.key} actions={scope}>\n <Form.Fields fieldMap={fieldMap} />\n </Form.FieldSet>\n <OllamaModels />\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n\nAssistantSettings.displayName = 'AssistantSettings';\n"],"mappings":";;;;;;;;;;;;;;;;;AAqBA,IAAM,cAAc,MAAM,YAAY,SAAS,OAAO,EAAE,CAAC,CAAC,KAAK,UAAU,MAAM,OAAO;;AAGtF,IAAM,uBAAuB;;;;;;AAO7B,IAAa,qBAAqB;CAChC,MAAM,UAAU,MAAM,sBAAsB,sBAAsB,aAAa;CAC/E,IAAI,CAAC,SACH,OAAO;CAGT,OAAO,oBAAC,qBAAD,EAA8B,QAAU,CAAA;AACjD;AAEA,IAAa,uBAAuB,EAAE,cAA2C;CAC/E,MAAM,EAAE,MAAM,QAAQ,eAAe,KAAK,QAAQ,GAAG;CAErD,MAAM,QAAQ,aAAa,QAAQ,KAAK;CACxC,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,MAAM,CAAC,SAAS,cAAc,SAAkC,CAAC,CAAC;CAGlE,gBAAgB;EACd,SAAc,WAAW,QAAQ,OAAO;CAC1C,GAAG,CAAC,OAAO,CAAC;CAGZ,gBAAgB;EACd,MAAM,WAAW,kBAAkB,KAAK,SAAS,WAAW,QAAQ,aAAa,GAAG,oBAAoB;EACxG,aAAa,cAAc,QAAQ;CACrC,GAAG,CAAC,OAAO,CAAC;CAEZ,MAAM,YAAY,IAAI,IAAI,MAAM,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC;CACjE,MAAM,SAAS,IAAI,IAAI,MAAM,OAAO,KAAK,UAAU,CAAC,MAAM,MAAM,KAAK,CAAC,CAAC;CACvE,MAAM,UAAU,OAAO,KAAK,MAAM,KAAK,CAAC,CAAC,QAAQ,SAAS,CAAC,UAAU,IAAI,IAAI,CAAC;CAE9E,MAAM,cAAc,aACjB,KAAa,WAAgC,YAAY;EACxD,YAAY,UAAU;GAAE,GAAG;IAAO,MAAM;EAAK,EAAE;EAC/C,IAAI;GACF,MAAM,OAAO;EACf,UAAU;GACR,YAAY,UAAU;IAAE,GAAG;KAAO,MAAM;GAAM,EAAE;EAClD;CACF,GACA,CAAC,CACH;CAEA,MAAM,aAAa,aAChB,WAAmB;EAClB,MAAM,UAAU,OAAO,KAAK;EAC5B,IAAI,QAAQ,WAAW,GACrB;EAEF,SAAS,EAAE;EACX,QAAQ,KAAK;EACb,YAAiB,eAAe,SAAS,WAAW,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;CAC9E,GACA,CAAC,SAAS,WAAW,CACvB;CAGA,MAAM,SAAS,MAAM,KAAK;CAC1B,MAAM,cAAc,YAAY,QAAQ,SAAS,CAAC,UAAU,IAAI,IAAI,MAAM,WAAW,MAAM,KAAK,SAAS,MAAM,EAAE;CACjH,MAAM,cAAc,OAAO,SAAS,KAAK,CAAC,UAAU,IAAI,MAAM,KAAK,CAAC,YAAY,SAAS,MAAM;CAC/F,MAAM,QAAQ,MAAM,OAAO,WAAW,KAAK,QAAQ,WAAW;CAE9D,OACE,qBAAC,KAAK,UAAN;EAAe,OAAO,EAAE,uBAAuB;EAA/C,UAAA,CACE,oBAAC,KAAK,OAAN;GAAY,YAAA;GAAW,OAAO,EAAE,iCAAiC;GAC9D,UAAA,MAAM,SAAS,YAAY,MAAM,QAEhC,oBAAC,KAAD;IAAG,WAAU;IAA2B,UAAA,EAAE,kCAAkC,EAAE,OAAO,MAAM,MAAM,CAAC;GAAK,CAAA,IACrG,QACF,oBAAC,KAAD;IAAG,WAAU;IAAyB,UAAA,EAAE,+BAA+B;GAAK,CAAA,IAK5E,qBAAC,MAAD;IAAM,SAAQ;IAAY,WAAU;IAApC,UAAA,CACG,MAAM,OAAO,KAAK,UAAU;KAC3B,MAAM,UAAU,OAAO,IAAI,MAAM,IAAI;KACrC,MAAM,QAAQ,MAAM,OAAO,MAAM;KACjC,MAAM,OAAO,MAAM,QAAQ,OAAO,YAAY,MAAM,IAAI,IAAI,KAAA;KAC5D,MAAM,cAAc,UAChB,QAAQ,WACN,EAAE,+BAA+B,EAAE,MAAM,YAAY,QAAQ,QAAQ,EAAE,CAAC,IACxE,EAAE,8BAA8B,IAClC,KAAA;KACJ,OACE,qBAAC,UAAD;MAEE,WAAU;MAFZ,UAAA,CAIE,qBAAC,OAAO,MAAR;OAAa,KAAI;OAAK,OAAM;OAA5B,UAAA;QACE,oBAAC,QAAD;SAAM,WAAU;SAA6B,UAAA,MAAM;QAAW,CAAA;QAC9D,oBAAC,OAAO,MAAR;SACE,MAAM,UAAU,uBAAuB;SACvC,UAAA;SACA,OAAO,UAAU,EAAE,8BAA8B,IAAI,EAAE,4BAA4B;SACnF,UAAU,QAAQ,MAAM;SACxB,eACE,KAAK,YAAY,MAAM,YACrB,SAAS,WAAW,UAAU,QAAQ,OAAO,MAAM,IAAI,IAAI,QAAQ,KAAK,MAAM,IAAI,CAAC,CACrF,CAAC,CAAC;QAEL,CAAA;QACD,oBAAC,OAAO,MAAR;SACE,MAAK;SACL,UAAA;SACA,OAAO,EAAE,8BAA8B;SACvC,UAAU,QAAQ,MAAM;SACxB,eACE,KAAK,YAAY,MAAM,YAAY,SAAS,WAAW,QAAQ,OAAO,MAAM,IAAI,CAAC,CAAC,CAAC,CAAC;QAEvF,CAAA;OACU;MACX,CAAA,IAAA,QAAQ,eAAe,UACvB,qBAAC,OAAO,MAAR;OAAa,KAAI;OAAK,OAAM;OAAS,YAAW;OAAhD,UAAA;QACG,QAAQ,oBAAC,QAAD;SAAM,WAAU;SAAiB,UAAA;QAAW,CAAA;QACpD,eAAe,oBAAC,QAAD;SAAM,WAAU;SAAqB,UAAA;QAAkB,CAAA;QACtE,SAAS,oBAAC,QAAD;SAAM,WAAU;SAA4B,UAAA,WAAW,KAAK;QAAQ,CAAA;OACnE;MAEP,CAAA,CAAA;KAjCH,GAAA,MAAM,IAiCH;IAEd,CAAC,GACA,QAAQ,KAAK,SAAS;KACrB,MAAM,WAAW,MAAM,MAAM;KAC7B,MAAM,SAAS,UAAU,QACrB,EAAE,mCAAmC,EAAE,SAAS,UAAU,QAAQ,EAAE,CAAC,IACpE,UAAU,UAAU,EAAE,+BAA+B;KAC1D,OACE,qBAAC,UAAD;MAEE,WAAU;MAFZ,UAAA,CAIE,qBAAC,OAAO,MAAR;OAAa,KAAI;OAAK,OAAM;OAA5B,UAAA,CACE,oBAAC,QAAD;QAAM,WAAU;QAA2C,UAAA;OAAW,CAAA,GACtE,oBAAC,OAAO,MAAR;QACE,MAAK;QACL,UAAA;QACA,OAAO,EAAE,8BAA8B;QACvC,eAAe,KAAK,SAAS,WAAW,QAAQ,OAAO,IAAI,CAAC;OAC7D,CAAA,CACU;MACb,CAAA,GAAA,oBAAC,QAAD;OAAM,WAAU;OAAyB,UAAA;MAAa,CAAA,CAC9C;KAbH,GAAA,IAaG;IAEd,CAAC,CACG;;EAEE,CAAA,GAEZ,qBAAC,KAAK,OAAN;GAAY,OAAO,EAAE,4BAA4B;GAAjD,UAAA,CAEG,OAAO,QAAQ,MAAM,MAAM,CAAA,CACzB,QAAQ,CAAC,UAAU,CAAC,UAAU,IAAI,IAAI,CAAC,CAAA,CACvC,KAAK,CAAC,MAAM,WACX,qBAAC,KAAD;IAAc,WAAU;IAAxB,UAAA;KACG;KAAK;KAAG,WAAW,KAAK;IACxB;GAFK,GAAA,IAEL,CACJ,GAIH,qBAAC,SAAS,MAAV;IACE,OAAO,YAAY,KAAK,UAAU;KAAE,OAAO;KAAM,OAAO;IAAK,EAAE;IAC/D,QAAQ;IACF;IACN,eAAe,EAAE,WAAW,QAAQ,IAAI;IACxC,OAAO,CAAC;IACR,gBAAgB,EAAE,OAAO,CAAC,YAAY,QAAQ,WAAW,IAAI;IAC7D,YAAY;IACZ,qBAAqB,EAAE,iBAAiB,SAAS,UAAU;IAC3D,UAAU,cAAc,aAAa,KAAA;IACrC,cAAc,SAAS,EAAE,qCAAqC,EAAE,KAAK,CAAC;IACtE,YAAW;IAXb,UAAA,CAaE,oBAAC,SAAS,SAAV;KAAkB,YAAW;KAAS,aAAa,EAAE,kCAAkC;IAAI,CAAA,GAC3F,qBAAC,SAAS,SAAV,EAAA,UAAA,CACE,oBAAC,SAAS,OAAV,EAAgB,aAAa,EAAE,kCAAkC,EAAI,CAAA,GACrE,oBAAC,SAAS,MAAV,EAAA,UACG,YAAY,KAAK,SAChB,oBAAC,SAAS,MAAV,EAA0B,MAAM;KAAE,OAAO;KAAM,OAAO;IAAK,EAAI,GAA3C,IAA2C,CAChE,EACY,CAAA,CACC,EAAA,CAAA,CACL;GACL,CAAA,CAAA;EACC,CAAA,CAAA;;AAEnB;;;;;AAMA,IAAM,cAAc,UAA0B;CAC5C,MAAM,OAAO,MAAM,MAAM,eAAe,CAAC,CAAC,EAAE,CAAC,KAAK;CAClD,OAAO,KAAK,SAAS,MAAM,GAAG,KAAK,MAAM,GAAG,GAAG,EAAE,KAAK;AACxD;;AAGA,IAAM,aAAa,aACjB,YAAY,SAAS,SAAS,SAAS,QAAQ,IAAI,KAAK,OAAQ,SAAS,aAAa,KAAK,SAAS,QAAS,GAAG,IAAI;AAEtH,IAAM,QAAQ;CAAC;CAAK;CAAM;CAAM;CAAM;AAAI;;AAG1C,IAAM,eAAe,UAA0B;CAC7C,IAAI,QAAQ;CACZ,IAAI,OAAO;CACX,OAAO,SAAS,QAAQ,OAAO,MAAM,SAAS,GAAG;EAC/C,SAAS;EACT,QAAQ;CACV;CACA,OAAO,GAAG,MAAM,QAAQ,SAAS,IAAI,IAAI,CAAC,EAAE,GAAG,MAAM;AACvD;AAEA,aAAa,cAAc;;;AChO3B,IAAM,iBAAiB,UAAmB,cACxC,mBAAmB,QAAQ,CAAA,CACxB,QAAQ,WAAW,CAAC,aAAa,UAAU,IAAI,OAAO,OAAO,CAAC,CAAA,CAC9D,KAAK,YAAY;CAAE,OAAO,OAAO;CAAO,OAAO,OAAO;AAAM,EAAE;AAEnE,IAAa,qBAAqB,EAAE,UAAU,kBAAkB,YAAoC;CAClG,MAAM,EAAE,MAAM,QAAQ,eAAe,KAAK,QAAQ,GAAG;CAIrD,MAAM,gBAAgB,MAAM,sBAAsB,sBAAsB,aAAa;CACrF,MAAM,gBAAgB,gBAAgB,SAAS,UAAU,SAAS;CAClE,MAAM,mBAAmB,gBAAgB,aAAa;CAGtD,MAAM,cAAc,aAAa,eAAe,SAAS,OAAO,UAAU;CAC1E,MAAM,YAAY,cAAc,IAAI,IAAI,YAAY,OAAO,KAAK,UAAU,MAAM,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC;CAIrG,MAAM,SAAS,eACN;EAAE,GAAG;EAAU,eAAe,gBAAgB,SAAS,eAAe,CAAC,CAAC,aAAa;CAAE,IAC9F,CAAC,UAAU,aAAa,CAC1B;CAGA,MAAM,WAAW,eACR;EAEL,iBAAiB,kBAAkB;GACjC,cAAc;GACd,SAAS;IACP;KAAE,OAAO,SAAS,KAAK;KAAI,OAAO,EAAE,8BAA8B;IAAE;IACpE;KAAE,OAAO,cAAc;KAAI,OAAO,EAAE,qBAAqB,iBAAiB,OAAO;IAAE;IACnF;KAAE,OAAO,SAAS,SAAS;KAAI,OAAO,EAAE,kCAAkC;IAAE;GAC9E;EACF,CAAC;EACD,sBAAsB,kBAAkB,EAAE,SAAS,cAAc,SAAS,KAAK,EAAE,EAAE,CAAC;EAEpF,wBAAwB,kBAAkB,EACxC,SAAS,cAAc,cAAc,IAAI,gBAAgB,YAAY,KAAA,CAAS,EAChF,CAAC;EACD,0BAA0B,kBAAkB,EAAE,SAAS,cAAc,SAAS,SAAS,EAAE,EAAE,CAAC;CAC9F,IACA;EAAC;EAAG;EAAe;EAAkB;EAAe;CAAS,CAC/D;CAEA,OACE,oBAAC,KAAK,MAAN;EACE,SAAQ;EACR,QAAQ,UAAU;EAClB,UAAU,CAAC;EACH;EACR,kBAAkB,WAAW,yBAAyB,MAAM;EAE5D,UAAA,oBAAC,KAAK,UAAN;GAAe,QAAA;GACb,UAAA,qBAAC,KAAK,SAAN,EAAA,UAAA,CACE,oBAAC,KAAK,UAAN;IAAe,OAAO,KAAK,QAAQ,QAAQ,KAAK,QAAQ;IAAK,SAAS;IACpE,UAAA,oBAAC,KAAK,QAAN,EAAuB,SAAW,CAAA;GACrB,CAAA,GACf,oBAAC,cAAD,CAAe,CAAA,CACH,EAAA,CAAA;EACD,CAAA;CACN,CAAA;AAEf;AAEA,kBAAkB,cAAc"}
|
|
1
|
+
{"version":3,"file":"chunk-AssistantSettings.mjs","names":[],"sources":["../../src/containers/AssistantSettings/OllamaModels.tsx","../../src/containers/AssistantSettings/AssistantSettings.tsx"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport React, { useCallback, useEffect, useState } from 'react';\n\nimport { Model, Provider } from '@dxos/ai';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { List, ListItem } from '@dxos/react-list';\nimport { Form } from '@dxos/react-ui-form';\nimport * as Button from '@dxos/react-ui/Button';\nimport * as Combobox from '@dxos/react-ui/Combobox';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\nimport * as Layout from '@dxos/react-ui/Layout';\n\nimport { meta } from '#meta';\nimport { AssistantCapabilities, Ollama } from '#types';\n\n/** Quick-pick model names (Ollama pull tags) sourced from the curated catalog. */\nconst QUICK_PICKS = Model.forProvider(Provider.ollama.id).map((model) => model.backend);\n\n/** Poll interval for the loaded-into-memory set while the settings panel is open. */\nconst LOADED_POLL_INTERVAL = 3_000;\n\n/**\n * Manage locally-installed Ollama models. The {@link AssistantCapabilities.OllamaManager}\n * capability is only contributed by the native (desktop) runtime, so this renders nothing in the\n * browser/mobile. Split from the section body so hooks are never called conditionally.\n */\nexport const OllamaModels = () => {\n const manager = Hooks.useOptionalCapability(AssistantCapabilities.OllamaManager);\n if (!manager) {\n return null;\n }\n\n return <OllamaModelsSection manager={manager} />;\n};\n\nexport const OllamaModelsSection = ({ manager }: { manager: Ollama.Manager }) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n // Explicit subscription: the list and badges must re-render as pulls/installs/loads land.\n const state = useAtomValue(manager.state);\n const [query, setQuery] = useState('');\n const [open, setOpen] = useState(false);\n // UI-level gate keyed by model name, guaranteeing a visible busy state across the click handler.\n const [pending, setPending] = useState<Record<string, boolean>>({});\n\n // Ensure the sidecar is up and list installed models on mount.\n useEffect(() => {\n void EffectEx.runPromise(manager.refresh);\n }, [manager]);\n\n // Poll the loaded-into-memory set so the panel reflects which model is resident in real time.\n useEffect(() => {\n const interval = setInterval(() => void EffectEx.runPromise(manager.refreshLoaded), LOADED_POLL_INTERVAL);\n return () => clearInterval(interval);\n }, [manager]);\n\n const installed = new Set(state.models.map((model) => model.name));\n const loaded = new Map(state.loaded.map((model) => [model.name, model]));\n const pulling = Object.keys(state.pulls).filter((name) => !installed.has(name));\n\n const withPending = useCallback(\n (key: string, action: () => Promise<void>) => async () => {\n setPending((prev) => ({ ...prev, [key]: true }));\n try {\n await action();\n } finally {\n setPending((prev) => ({ ...prev, [key]: false }));\n }\n },\n [],\n );\n\n const handlePull = useCallback(\n (target: string) => {\n const trimmed = target.trim();\n if (trimmed.length === 0) {\n return;\n }\n setQuery('');\n setOpen(false);\n void withPending(trimmed, () => EffectEx.runPromise(manager.pull(trimmed)))();\n },\n [manager, withPending],\n );\n\n // Caller-driven filtering: curated picks not yet installed, matching the typed query.\n const filter = query.trim();\n const suggestions = QUICK_PICKS.filter((pick) => !installed.has(pick) && (filter === '' || pick.includes(filter)));\n const offerCustom = filter.length > 0 && !installed.has(filter) && !suggestions.includes(filter);\n const empty = state.models.length === 0 && pulling.length === 0;\n\n return (\n <Form.FieldSet label={t('settings.ollama.title')}>\n <Form.Field standalone label={t('settings.ollama.installed.label')}>\n {state.kind === 'failed' && state.error ? (\n // Connection-level failure has no associated model, so it shows inline as the row content.\n <p className='text-sm text-error-text'>{t('settings.ollama.failed.message', { error: state.error })}</p>\n ) : empty ? (\n <p className='text-sm text-fg-muted'>{t('settings.ollama.empty.message')}</p>\n ) : (\n // Plain `List`/`ListItem` (non-select), with the trigger editor's \"fatter\" two-line row\n // treatment: a name line plus a secondary meta line, on a surface-styled row. Rows are\n // actioned via their trailing buttons (no selection semantics).\n <List variant='unordered' className='flex flex-col gap-1 grow w-full text-left'>\n {state.models.map((model) => {\n const running = loaded.get(model.name);\n const error = state.errors[model.name];\n const size = model.size != null ? formatBytes(model.size) : undefined;\n const loadedLabel = running\n ? running.sizeVram\n ? t('settings.ollama.loaded.vram', { size: formatBytes(running.sizeVram) })\n : t('settings.ollama.loaded.label')\n : undefined;\n return (\n <ListItem\n key={model.name}\n className='flex flex-col gap-trim-xs rounded-sm dx-input-surface px-trim-sm py-trim-xs w-full'\n >\n <Layout.Flex gap='sm' align='center'>\n <span className='grow truncate font-medium'>{model.name}</span>\n <Button.Root\n icon={running ? 'ph--eject--regular' : 'ph--play--regular'}\n iconOnly\n label={running ? t('settings.ollama.unload.label') : t('settings.ollama.load.label')}\n disabled={pending[model.name]}\n onClick={() =>\n void withPending(model.name, () =>\n EffectEx.runPromise(running ? manager.unload(model.name) : manager.load(model.name)),\n )()\n }\n />\n <Button.Root\n icon='ph--trash--regular'\n iconOnly\n label={t('settings.ollama.remove.label')}\n disabled={pending[model.name]}\n onClick={() =>\n void withPending(model.name, () => EffectEx.runPromise(manager.remove(model.name)))()\n }\n />\n </Layout.Flex>\n {(size || loadedLabel || error) && (\n <Layout.Flex gap='sm' align='center' classNames='text-sm'>\n {size && <span className='text-fg-muted'>{size}</span>}\n {loadedLabel && <span className='text-success-text'>{loadedLabel}</span>}\n {error && <span className='truncate text-error-text'>{shortError(error)}</span>}\n </Layout.Flex>\n )}\n </ListItem>\n );\n })}\n {pulling.map((name) => {\n const progress = state.pulls[name];\n const status = progress?.total\n ? t('settings.ollama.pulling.message', { percent: percentOf(progress) })\n : (progress?.status ?? t('settings.ollama.pulling.label'));\n return (\n <ListItem\n key={name}\n className='flex flex-col gap-trim-xs rounded-sm dx-input-surface px-trim-sm py-trim-xs w-full'\n >\n <Layout.Flex gap='sm' align='center'>\n <span className='grow truncate font-medium text-fg-muted'>{name}</span>\n <Button.Root\n icon='ph--x--regular'\n iconOnly\n label={t('settings.ollama.cancel.label')}\n onClick={() => void EffectEx.runPromise(manager.cancel(name))}\n />\n </Layout.Flex>\n <span className='text-sm text-fg-muted'>{status}</span>\n </ListItem>\n );\n })}\n </List>\n )}\n </Form.Field>\n\n <Form.Field label={t('settings.ollama.pull.label')}>\n {/* Pull failures for not-yet-installed models surface here (no model row to attach to). */}\n {Object.entries(state.errors)\n .filter(([name]) => !installed.has(name))\n .map(([name, error]) => (\n <p key={name} className='text-xs text-error-text'>\n {name}: {shortError(error)}\n </p>\n ))}\n {/* Root value is held empty so the trigger always shows the placeholder; the live text is\n the separate `query` driving the search field and suggestion filter. A query that names no\n pick is offered as the create row, which pulls it. */}\n <Combobox.Root\n items={suggestions.map((pick) => ({ value: pick, label: pick }))}\n filter={null}\n open={open}\n onOpenChange={({ open }) => setOpen(open)}\n value={[]}\n onValueChange={({ value: [pick] }) => pick && handlePull(pick)}\n inputValue={query}\n onInputValueChange={({ inputValue }) => setQuery(inputValue)}\n onCreate={offerCustom ? handlePull : undefined}\n createLabel={(name) => t('settings.ollama.pull-custom.label', { name })}\n createIcon='ph--download-simple--regular'\n >\n <Combobox.Trigger classNames='w-full' placeholder={t('settings.ollama.pull.placeholder')} />\n <Combobox.Content>\n <Combobox.Input placeholder={t('settings.ollama.pull.placeholder')} />\n <Combobox.List>\n {suggestions.map((pick) => (\n <Combobox.Item key={pick} item={{ value: pick, label: pick }} />\n ))}\n </Combobox.List>\n </Combobox.Content>\n </Combobox.Root>\n </Form.Field>\n </Form.FieldSet>\n );\n};\n\n/**\n * Condense a verbose service error for inline display: drop Ollama's \"(checked: <paths>)\" path dump\n * and cap the length. The full message is still logged to the console.\n */\nconst shortError = (error: string): string => {\n const head = error.split(/\\s*\\(checked:/)[0].trim();\n return head.length > 160 ? `${head.slice(0, 160)}…` : head;\n};\n\n/** Percent complete for an in-flight pull, or 0 when totals are not yet known. */\nconst percentOf = (progress: Ollama.ModelsState['pulls'][string] | undefined): number =>\n progress && progress.total && progress.total > 0 ? Math.round(((progress.completed ?? 0) / progress.total) * 100) : 0;\n\nconst UNITS = ['B', 'KB', 'MB', 'GB', 'TB'];\n\n/** Human-readable byte size (binary units). */\nconst formatBytes = (bytes: number): string => {\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n return `${value.toFixed(unit === 0 ? 0 : 1)} ${UNITS[unit]}`;\n};\n\nOllamaModels.displayName = 'OllamaModels';\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { useAtomValue } from '@effect/atom-react/Hooks';\nimport React, { useMemo } from 'react';\n\nimport { Provider } from '@dxos/ai';\nimport * as Hooks from '@dxos/app-framework/Hooks';\nimport type * as AppSurface from '@dxos/app-toolkit/AppSurface';\nimport { type DXN } from '@dxos/keys';\nimport { Form, type FormFieldMap, createSelectField } from '@dxos/react-ui-form';\nimport * as UiHooks from '@dxos/react-ui/Hooks';\n\nimport { meta } from '#meta';\nimport { Assistant, AssistantCapabilities, Ollama } from '#types';\n\nimport { presetsForProvider, resolveProvider } from '../../chat-model/index.ts';\nimport { OllamaModels } from './OllamaModels.tsx';\n\nexport type AssistantSettingsProps = AppSurface.SettingsProps<Assistant.Settings>;\n\n// The per-provider model-default options must match the chat picker (usePresets): the catalog models\n// served by the provider, restricted to installed models when `installed` is supplied (local sidecar).\nconst presetOptions = (provider: DXN.DXN, installed?: ReadonlySet<string>) =>\n presetsForProvider(provider)\n .filter((preset) => !installed || installed.has(preset.backend))\n .map((preset) => ({ value: preset.model, label: preset.label }));\n\nexport const AssistantSettings = ({ settings, onSettingsChange, scope }: AssistantSettingsProps) => {\n const { t } = UiHooks.useTranslation(meta.profile.key);\n\n // The Ollama manager is the bundled sidecar (desktop only). Its presence selects the local\n // provider: the managed `built-in` vs. an external `ollama` server.\n const ollamaManager = Hooks.useOptionalCapability(AssistantCapabilities.OllamaManager);\n const localProvider = ollamaManager ? Provider.builtIn : Provider.ollama;\n const localProviderKey = ollamaManager ? 'built-in' : 'ollama';\n\n // Installed models reported by the bundled sidecar; restricts the local default to present models.\n const localModels = useAtomValue(ollamaManager?.state ?? Ollama.emptyState);\n const installed = useMemo(() => new Set(localModels.models.map((model) => model.name)), [localModels]);\n\n // Reconcile a stored provider with the runtime (`ollama` → `built-in` on desktop) so the\n // dropdown shows a concrete, available selection rather than blank.\n const values = useMemo(\n () => ({ ...settings, modelProvider: resolveProvider(settings.modelProvider, !!ollamaManager) }),\n [settings, ollamaManager],\n );\n\n // Form `fieldMap` is keyed by dotted JSON path, so nested struct leaves are addressed directly.\n const fieldMap = useMemo<FormFieldMap>(\n () => ({\n // `defaultLabel: null` drops the sentinel \"Default\" option — a provider is always concrete.\n 'modelProvider': createSelectField({\n defaultLabel: null,\n options: [\n { value: Provider.edge.id, label: t('settings.provider.edge.label') },\n { value: localProvider.id, label: t(`settings.provider.${localProviderKey}.label`) },\n { value: Provider.lmStudio.id, label: t('settings.provider.lmstudio.label') },\n ],\n }),\n 'modelDefaults.edge': createSelectField({ options: presetOptions(Provider.edge.id) }),\n // `built-in` and `ollama` share the `ollama` default key; the managed sidecar restricts to installed.\n 'modelDefaults.ollama': createSelectField({\n options: presetOptions(localProvider.id, ollamaManager ? installed : undefined),\n }),\n 'modelDefaults.lmstudio': createSelectField({ options: presetOptions(Provider.lmStudio.id) }),\n }),\n [t, localProvider, localProviderKey, ollamaManager, installed],\n );\n\n return (\n <Form.Root\n variant='settings'\n schema={Assistant.Settings}\n readonly={!onSettingsChange}\n values={values}\n onValuesChanged={(values) => onSettingsChange?.(() => values)}\n >\n <Form.Viewport scroll>\n <Form.Content>\n <Form.FieldSet label={meta.profile.name ?? meta.profile.key} actions={scope}>\n <Form.Fields fieldMap={fieldMap} />\n </Form.FieldSet>\n <OllamaModels />\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n\nAssistantSettings.displayName = 'AssistantSettings';\n"],"mappings":"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"}
|