@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-chat-model.mjs","names":[],"sources":["../../src/chat-model/outbox.ts","../../src/util/error-cause.ts","../../src/chat-model/prompt.ts","../../src/chat-model/thread.ts","../../src/chat-model/chat-model.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Atom from 'effect/reactivity/Atom';\nimport type * as AtomRegistry from 'effect/reactivity/AtomRegistry';\n\nimport { Obj } from '@dxos/echo';\nimport { BaseError } from '@dxos/errors';\nimport { log } from '@dxos/log';\nimport { type ContentBlock } from '@dxos/types';\n\n/** How far this client got with a prompt; what the agent did with it is read from the feed. */\nexport type OutboxState = 'sending' | 'submitted' | 'failed';\n\n/**\n * A prompt the reader sent from this chat, held from the moment of submit — before anything is\n * persisted — so the thread can show it at once. The thread reconciles it against the feed's queue\n * entry and the turn the agent runs from it (see `projectThread`).\n */\nexport type OutboxEntry = {\n /** The identity of the prompt's thread row for its whole life, so the row never remounts. */\n readonly id: string;\n readonly created: string;\n /** The prompt's content exactly as it is submitted, so the persisted copy renders the same. */\n readonly blocks: readonly ContentBlock.Any[];\n /** Feed messages that existed at submit: an identical earlier prompt is never this one's echo. */\n readonly known: ReadonlySet<string>;\n readonly state: OutboxState;\n readonly error?: Error;\n};\n\n/** Hands one prompt to the agent: resolves once the agent holds it, rejects if it never will. */\nexport type OutboxDispatch<T> = (payload: T) => Promise<void>;\n\n/** A dispatch stopped before the prompt reached the agent, by the reader: the prompt is dropped, not failed. */\nexport class PromptCancelledError extends BaseError.extend(\n 'PromptCancelledError',\n 'Prompt cancelled before it was sent.',\n) {}\n\n/**\n * The prompts this client has sent, in submit order.\n *\n * Dispatches run one at a time, each waiting only until the previous prompt is in the agent's hands\n * (not for its turn): two submits racing to the agent would otherwise reach its queue in whichever\n * order their round trips landed. A failure does not hold up the prompts behind it.\n */\nexport class Outbox<T> {\n // Kept alive: the outbox is the only record of a prompt still being dispatched, and an atom nothing\n // subscribes to is reset by the registry — between two submits with the thread unmounted, say.\n readonly entries = Atom.make<readonly OutboxEntry[]>([]).pipe(Atom.keepAlive);\n\n readonly #payloads = new Map<string, T>();\n #tail: Promise<void> = Promise.resolve();\n\n constructor(\n private readonly _registry: AtomRegistry.AtomRegistry,\n private readonly _dispatch: OutboxDispatch<T>,\n ) {}\n\n /** Settles once every dispatch scheduled so far has settled. */\n get idle(): Promise<void> {\n return this.#tail;\n }\n\n get(id: string): OutboxEntry | undefined {\n return this._registry.get(this.entries).find((entry) => entry.id === id);\n }\n\n /** Appends the prompt synchronously, then dispatches it behind any already sending. */\n add(payload: T, { blocks, known }: Pick<OutboxEntry, 'blocks' | 'known'>): OutboxEntry {\n const entry: OutboxEntry = {\n id: Obj.ID.random(),\n created: new Date().toISOString(),\n blocks,\n known,\n state: 'sending',\n };\n this.#payloads.set(entry.id, payload);\n this._registry.update(this.entries, (entries) => [...entries, entry]);\n this.#schedule(entry.id);\n return entry;\n }\n\n remove(id: string): void {\n this.#payloads.delete(id);\n this._registry.update(this.entries, (entries) => entries.filter((entry) => entry.id !== id));\n }\n\n #schedule(id: string): void {\n this.#tail = this.#tail.then(() => this.#send(id));\n }\n\n async #send(id: string): Promise<void> {\n const payload = this.#payloads.get(id);\n if (payload === undefined || this.get(id)?.state !== 'sending') {\n return;\n }\n\n try {\n await this._dispatch(payload);\n this.#patch(id, { state: 'submitted' });\n } catch (err) {\n if (err instanceof PromptCancelledError) {\n this.remove(id);\n return;\n }\n\n log.warn('prompt was not sent', { error: err });\n this.#patch(id, { state: 'failed', error: err instanceof Error ? err : new Error(String(err)) });\n }\n }\n\n #patch(id: string, patch: Pick<OutboxEntry, 'state' | 'error'>): void {\n this._registry.update(this.entries, (entries) =>\n entries.map((entry) => (entry.id === id ? { ...entry, ...patch } : entry)),\n );\n }\n}\n","//\n// Copyright 2026 DXOS.org\n//\n\n/** Walk an error's `cause` chain and return the first value matching `predicate`. */\nexport const findInCause = <T>(error: unknown, predicate: (value: unknown) => value is T): T | undefined => {\n let current: unknown = error;\n // Guard against self-referential / cyclic `cause` chains so traversal always terminates.\n const visited = new Set<unknown>();\n while (current) {\n if (visited.has(current)) {\n break;\n }\n visited.add(current);\n if (predicate(current)) {\n return current;\n }\n current = current instanceof Error ? current.cause : undefined;\n }\n return undefined;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { ContentBlock } from '@dxos/types';\nimport { trim } from '@dxos/util';\n\n/** Ephemeral, per-request context captured at submit time (not part of the durable chat state). */\nexport type ChatRequestContext = {\n selection?: {\n /** Anchor strings (`\"${from}:${to}\"` cursor pairs) the text was resolved from; enables future in-place actions. */\n anchors: string[];\n text: string;\n };\n};\n\n/**\n * Prompt content for a request: the bare message, or — when the request carries selection context —\n * a synthetic block (system-generated user-turn content, hidden from the summary view) ahead of it.\n * A synthetic request is itself system-generated (a completed inline flow reporting itself), so it\n * becomes one synthetic block rather than words the reader appears to have typed.\n */\nexport const createPromptContent = (request: {\n message: string;\n disposition?: ContentBlock.Text['disposition'];\n context?: ChatRequestContext;\n}): string | ContentBlock.Any[] => {\n if (request.disposition === 'synthetic') {\n return [ContentBlock.Text.make({ text: request.message, disposition: 'synthetic' })];\n }\n\n const selection = request.context?.selection;\n if (!selection?.text.length) {\n return request.message;\n }\n\n return [\n ContentBlock.Text.make({\n disposition: 'synthetic',\n text: trim`\n The user's current selection in the document they are viewing:\n <selection>\n ${selection.text}\n </selection>\n `,\n }),\n ContentBlock.Text.make({ text: request.message }),\n ];\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Array from 'effect/Array';\nimport * as Order from 'effect/Order';\n\nimport { type Alarm, isConsumed, isInFlight, isQueued } from '@dxos/assistant';\nimport { Annotation, Feed, Obj } from '@dxos/echo';\nimport { DeliveryAnnotation, type DeliveryStatus } from '@dxos/react-ui-assistant/types';\nimport { type ContentBlock, Message } from '@dxos/types';\n\nimport { type OutboxEntry } from './outbox.ts';\n\n/**\n * Append order for {@link Feed.history}, which walks lineage positionally rather than by time.\n *\n * Position is authoritative but server-assigned: locally-written blocks — and every block until a\n * position authority acknowledges them — report `+Infinity`, and a query returns an unordered set, so\n * position alone leaves them in arbitrary order. `created` breaks those ties: sound locally, where one\n * conversation's messages come off one clock, and consulted only when position cannot decide.\n */\nexport const byAppendOrder: Order.Order<Message.Message> = (a, b) => {\n const positionA = Feed.getPosition(a);\n const positionB = Feed.getPosition(b);\n if (positionA !== positionB) {\n return positionA < positionB ? -1 : 1;\n }\n return a.created < b.created ? -1 : a.created > b.created ? 1 : 0;\n};\n\n/** What the thread knows about a row standing for a prompt on its way to the agent. */\nexport type DeliveryRow = {\n status: DeliveryStatus;\n /** The client's outbox entry, for a prompt sent from this chat. */\n outboxId?: string;\n /** The agent's queue entry in the feed, once it has landed. */\n entryId?: string;\n /** The user message of the turn the agent ran from it, once that has landed. */\n turnId?: string;\n};\n\nexport type ThreadProjection = {\n /**\n * The rows to render, in order: the turns, then the prompts the agent has not taken up yet.\n * A prompt is ONE row from submit to its turn — keyed by its outbox id, or its queue entry's id\n * after a reload — so it never shows twice and never remounts as the copies of it land.\n */\n messages: Message.Message[];\n /** Delivery state of the rows standing for prompts, by row id. */\n delivery: ReadonlyMap<string, DeliveryRow>;\n /** Prompts still waiting for the agent (sent or delivered, not yet read). */\n queued: number;\n /** How many rows at the end of {@link messages} are prompts not yet in the history. */\n tail: number;\n};\n\n/** A chat with nothing in it yet. */\nexport const EMPTY_THREAD: ThreadProjection = { messages: [], delivery: new Map(), queued: 0, tail: 0 };\n\n/**\n * The turns a thread should render: those reachable from the feed's head, so a rewind's abandoned turns\n * disappear from the view exactly as they disappear from the model's history — followed by the prompts\n * still on their way to the agent, each with its delivery status.\n *\n * A pending `rewindFrom` truncates the view to what precedes it — the rewound-to prompt and everything\n * after it, since the point of rewinding to a prompt is to re-ask it. The pointer is cleared by the next\n * append (which turns the fork into lineage), so a rewind that is never followed through stays visible\n * as a truncated thread with the prompt restored in the composer.\n *\n * A prompt sent from this chat ({@link OutboxEntry}) is reconciled against the feed in three steps:\n * the client holds it (`sent`), the agent's queue entry lands (`delivered`), and the agent takes it\n * up, marking the entry in flight and appending the turn's own user message (`read`). Each copy is\n * matched to the earliest unclaimed feed message carrying the prompt's blocks that did not exist at\n * submit, so identical prompts pair up in order.\n */\nexport const projectThread = ({\n feedMessages,\n pendingMessages = [],\n rewindFrom,\n outbox = [],\n}: {\n feedMessages: readonly Message.Message[];\n /** Messages produced by the current turn that are not in the feed yet. */\n pendingMessages?: readonly Message.Message[];\n /** Earliest message a pending rewind discards. */\n rewindFrom?: string;\n /** Prompts sent from this chat, in submit order. */\n outbox?: readonly OutboxEntry[];\n}): ThreadProjection => {\n const all = Array.dedupeWith([...feedMessages, ...pendingMessages], ({ id: a }, { id: b }) => a === b);\n // A turn's messages that the feed has not caught up with yet continue from the thread's head, so\n // they follow the lineage rather than join it: with no parent of their own, lineage would chain them\n // to whatever sorts before them, which after a rewind is the abandoned reply.\n const feedIds = new Set(feedMessages.map((message) => message.id));\n const unrecorded = Array.sort(\n Array.dedupeWith(\n pendingMessages.filter((message) => !feedIds.has(message.id) && !isQueued(message)),\n ({ id: a }, { id: b }) => a === b,\n ),\n byAppendOrder,\n );\n // A queue entry is not a turn: the turn the agent runs from one appends its own user message, so an\n // entry never joins the history. Until the agent takes it up, it is a row after the history instead.\n const sorted = Array.sort(\n feedMessages.filter((message) => !isQueued(message)),\n byAppendOrder,\n );\n const entries = Array.sort(\n all.filter((message) => isQueued(message)),\n byAppendOrder,\n );\n\n const history = projectHistory(sorted, unrecorded, rewindFrom);\n // The turn's own user message may still be only in the streaming turn, ahead of the feed.\n const turnCandidates = [...sorted, ...unrecorded];\n const claimed = new Set<string>();\n const claim = (candidates: readonly Message.Message[], entry: OutboxEntry): Message.Message | undefined => {\n const match = candidates.find(\n (message) => !claimed.has(message.id) && !entry.known.has(message.id) && carries(message, entry.blocks),\n );\n if (match) {\n claimed.add(match.id);\n }\n return match;\n };\n\n const delivery = new Map<string, DeliveryRow>();\n // Turns that are this chat's own prompts, swapped for the prompt's row so the row keeps its identity.\n const turns = new Map<string, Message.Message>();\n const tail: Message.Message[] = [];\n const sent: Message.Message[] = [];\n for (const entry of outbox) {\n const queueEntry = claim(entries, entry);\n const turn = claim(turnCandidates, entry);\n const status: DeliveryStatus =\n turn || (queueEntry && (isInFlight(queueEntry) || isConsumed(queueEntry)))\n ? 'read'\n : queueEntry\n ? 'delivered'\n : entry.state === 'failed'\n ? 'failed'\n : 'sent';\n\n const row = deliveryRow(entry.id, turn ?? queueEntry ?? entry, entry.created, status);\n delivery.set(entry.id, { status, outboxId: entry.id, entryId: queueEntry?.id, turnId: turn?.id });\n if (turn) {\n turns.set(turn.id, row);\n } else {\n // Read but with no turn yet: the turn's message replicates separately and can trail the mark.\n sent.push(row);\n }\n }\n\n // Waiting entries this chat did not send (another device, or a reload) — they predate the outbox.\n for (const entry of entries) {\n if (!claimed.has(entry.id) && !isConsumed(entry) && !isInFlight(entry)) {\n tail.push(deliveryRow(entry.id, entry, entry.created, 'delivered'));\n delivery.set(entry.id, { status: 'delivered', entryId: entry.id });\n }\n }\n tail.push(...sent);\n\n const messages = [...history.map((message) => turns.get(message.id) ?? message), ...tail];\n const queued = tail.filter((row) => {\n const status = delivery.get(row.id)?.status;\n return status === 'sent' || status === 'delivered';\n }).length;\n\n return { messages, delivery, queued, tail: tail.length };\n};\n\nconst projectHistory = (\n sorted: readonly Message.Message[],\n unrecorded: readonly Message.Message[],\n rewindFrom: string | undefined,\n): Message.Message[] => {\n if (rewindFrom !== undefined) {\n const index = sorted.findIndex((message) => message.id === rewindFrom);\n if (index === 0) {\n // Rewound to the first turn: nothing precedes it.\n return collapseToolRuns(unrecorded);\n }\n // Once a recorded message continues from the rewind head the fork is lineage, whether or not the\n // pointer has been cleared yet.\n if (index > 0 && !sorted.slice(index + 1).some((message) => Feed.getParent(message) === sorted[index - 1].id)) {\n return collapseToolRuns([...Feed.history(sorted, { head: sorted[index - 1].id }).items, ...unrecorded]);\n }\n // Not present — a stale pointer (e.g. the message never replicated) — or already continued; fall\n // through to the feed's own lineage rather than blanking the thread.\n }\n\n return collapseToolRuns([...Feed.history(sorted).items, ...unrecorded]);\n};\n\n/**\n * Whether a feed message is a copy of the prompt `blocks`: the queue entry carries them as they are,\n * and the turn's user message carries them after whatever the agent prepends (object versions).\n */\nconst carries = (message: Message.Message, blocks: readonly ContentBlock.Any[]): boolean => {\n if (message.sender.role !== 'user') {\n return false;\n }\n\n const texts = message.blocks.flatMap((block) => (block._tag === 'text' ? [textKey(block)] : []));\n const wanted = blocks.flatMap((block) => (block._tag === 'text' ? [textKey(block)] : []));\n return wanted.length > 0 && wanted.every((key, index) => texts[texts.length - wanted.length + index] === key);\n};\n\nconst textKey = (block: ContentBlock.Text): string => `${block.disposition ?? ''}:${block.text}`;\n\n/** The row last built for a source, reused while its status holds so an unchanged row is not re-rendered. */\nconst deliveryRows = new WeakMap<object, { id: string; status: DeliveryStatus; row: Message.Message }>();\n\n/**\n * The row a prompt renders as: its content from the latest copy of it, under the row's own id, marked\n * with its delivery status for the renderer's ticks.\n */\nconst deliveryRow = (\n id: string,\n source: Pick<Message.Message, 'blocks'>,\n created: string,\n status: DeliveryStatus,\n): Message.Message => {\n const cached = deliveryRows.get(source);\n if (cached && cached.id === id && cached.status === status) {\n return cached.row;\n }\n\n const row = Message.make({ id, created, sender: { role: 'user' }, blocks: [...source.blocks] });\n Obj.update(row, (row) => Annotation.set(row, DeliveryAnnotation, status));\n deliveryRows.set(source, { id, status, row });\n return row;\n};\n\n/**\n * Alarms that have woken the agent since the last prompt the user typed: the count the runtime caps at\n * `Alarm.MAX_SELF_WAKES`, read from the feed so the status can show it.\n */\nexport const projectSelfWakes = ({\n feedAlarms,\n messages,\n}: {\n feedAlarms: readonly Alarm.Alarm[];\n messages: readonly Message.Message[];\n}): number => {\n const lastPrompt = messages.findLast(\n (message) =>\n message.sender.role === 'user' &&\n message.blocks.some((block) => block._tag === 'text' && block.disposition !== 'synthetic'),\n );\n const since = lastPrompt?.created ?? '';\n return feedAlarms.filter((alarm) => isConsumed(alarm) && alarm.created >= since).length;\n};\n\n/**\n * The alarms still waiting to fire, earliest first: those the agent has not consumed and (for a\n * cancelled one) not removed from the feed.\n */\nexport const projectAlarms = ({ feedAlarms }: { feedAlarms: readonly Alarm.Alarm[] }): Alarm.Alarm[] =>\n Array.sort(\n feedAlarms.filter((alarm) => !isConsumed(alarm)),\n Order.mapInput(Order.Number, (alarm: Alarm.Alarm) => alarm.wakeAt),\n );\n\n/**\n * Blocks that are the machinery of a turn rather than anything the reader wrote or read.\n *\n * `reasoning` and `status` are machinery too: the model explains itself and narrates what it is\n * about to do between calls, so a run of calls is interleaved with both and treating either as prose\n * would split every run into one panel per call. A tool result recovered across a reload arrives as\n * a synthetic text block rather than a tool result (its call id can no longer be answered), which is\n * machinery on the same grounds.\n */\nconst TOOL_BLOCKS = new Set(['toolCall', 'toolResult', 'stats', 'reasoning', 'status']);\n\nconst isMachinery = (block: Message.Message['blocks'][number]): boolean =>\n TOOL_BLOCKS.has(block._tag) ||\n (block._tag === 'text' && (block as { disposition?: string }).disposition === 'synthetic');\n\nconst isToolOnly = (message: Message.Message): boolean =>\n message.blocks.length > 0 && message.blocks.every(isMachinery);\n\n/** A folded run and the messages it was folded from, keyed by the run's first message. */\nconst foldedRuns = new WeakMap<Message.Message, { run: readonly Message.Message[]; folded: Message.Message }>();\n\n/**\n * The run folded into one message, reusing the previous fold while the run holds the same messages:\n * the thread re-projects on every streamed block, and a fresh object per pass made every tool panel\n * in the thread look changed, re-rendering all of them for each block of the current turn.\n */\nconst foldRun = (run: readonly Message.Message[]): Message.Message => {\n const [first] = run;\n const cached = foldedRuns.get(first);\n if (cached && cached.run.length === run.length && cached.run.every((message, index) => message === run[index])) {\n return cached.folded;\n }\n\n const folded = { ...first, blocks: run.flatMap((entry) => entry.blocks) } as Message.Message;\n foldedRuns.set(first, { run, folded });\n return folded;\n};\n\n/**\n * Folds each run of tool-only messages into one, so a multi-step turn renders as a single panel.\n *\n * The runtime delivers one block per message, so without this a turn is one row per call and the\n * panel's summary cannot count the run it belongs to. The run's first message supplies the identity,\n * keeping the row stable as the run grows and leaving `data-object-id` pointing at a real object.\n */\nexport const collapseToolRuns = (messages: readonly Message.Message[]): Message.Message[] => {\n const collapsed: Message.Message[] = [];\n // The messages each entry was folded from, so a later result can join the fold that holds its call.\n const sources: (readonly Message.Message[])[] = [];\n for (let index = 0; index < messages.length; index++) {\n const message = messages[index];\n if (!isToolOnly(message)) {\n collapsed.push(message);\n sources.push([message]);\n continue;\n }\n\n let end = index;\n while (end + 1 < messages.length && isToolOnly(messages[end + 1])) {\n end++;\n }\n const run = messages.slice(index, end + 1);\n index = end;\n\n // A result held apart from its call by the request card that asked about it joins the call's panel; on its\n // own it renders as an empty row.\n const results = run.flatMap((entry) => entry.blocks);\n const callIds = new Set(results.map((block) => (block._tag === 'toolResult' ? block.toolCallId : undefined)));\n const caller = results.every((block) => block._tag === 'toolResult')\n ? collapsed.findLastIndex((earlier) =>\n earlier.blocks.some((block) => block._tag === 'toolCall' && callIds.has(block.toolCallId)),\n )\n : -1;\n if (caller >= 0) {\n sources[caller] = [...sources[caller], ...run];\n collapsed[caller] = foldRun(sources[caller]);\n } else {\n collapsed.push(run.length === 1 ? message : foldRun(run));\n sources.push(run);\n }\n }\n\n return collapsed;\n};\n\n/**\n * What a rewind to `messageId` implies: the messages to discard from, and the prompt text to restore so\n * the user can edit and resend it.\n *\n * Returns `undefined` when the message is absent, so a stale click is a no-op rather than a truncation.\n */\nexport const resolveRewind = (\n messages: readonly Message.Message[],\n messageId: string,\n): { rewindFrom: string; text: string } | undefined => {\n const message = messages.find((candidate) => candidate.id === messageId);\n if (!message) {\n return undefined;\n }\n return { rewindFrom: messageId, text: Message.extractText(message) };\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AiError from 'effect/ai/AiError';\nimport * as Cause from 'effect/Cause';\nimport * as Effect from 'effect/Effect';\nimport * as Exit from 'effect/Exit';\nimport * as Fiber from 'effect/Fiber';\nimport * as Layer from 'effect/Layer';\nimport * as Option from 'effect/Option';\nimport * as Atom from 'effect/reactivity/Atom';\nimport * as AtomRegistry from 'effect/reactivity/AtomRegistry';\nimport * as Stream from 'effect/Stream';\n\nimport { AiModelNotAvailableError, type AiService, Model, type OpaqueToolkit } from '@dxos/ai';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport {\n AiContext,\n AiSession,\n Harness,\n ToolExecutionServices,\n createSystemPrompt,\n formatSystemPrompt,\n} from '@dxos/assistant';\nimport * as Agent from '@dxos/assistant/Agent';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport type * as Credential from '@dxos/compute/Credential';\nimport type * as Instructions from '@dxos/compute/Instructions';\nimport * as Operation from '@dxos/compute/Operation';\nimport type * as Process from '@dxos/compute/Process';\nimport type * as ServiceResolver from '@dxos/compute/ServiceResolver';\nimport * as Skill from '@dxos/compute/Skill';\nimport * as Trace from '@dxos/compute/Trace';\nimport { Database, Feed, Filter, Obj, Query, Ref, type Registry } from '@dxos/echo';\nimport { UsageQuotaExceededError } from '@dxos/edge-client';\nimport * as EffectEx from '@dxos/effect/EffectEx';\nimport { DXN } from '@dxos/keys';\nimport { log } from '@dxos/log';\nimport { ContentBlock, Message } from '@dxos/types';\nimport { markWork } from '@dxos/util';\n\nimport { TurnReviewSkill } from '#skills';\nimport { AssistantOperation } from '#types';\n\nimport { findInCause } from '../util/error-cause.ts';\nimport { Outbox, type OutboxEntry, PromptCancelledError } from './outbox.ts';\nimport { providerForModel } from './presets.ts';\nimport { type ChatRequestContext, createPromptContent } from './prompt.ts';\nimport { EMPTY_THREAD, type ThreadProjection, projectThread } from './thread.ts';\n\n/**\n * Space-scoped services materialised by the layer passed into\n * {@link ChatModel}. Mirrors the tag list that\n * {@link useChatModel} passes to {@link ServiceResolver.provide}.\n */\nexport type SpaceServices =\n | Database.Service\n | Credential.CredentialsService\n | AiService.AiService\n | AgentService.AgentService\n | Registry.Service\n | OpaqueToolkit.OpaqueToolkitProvider;\n\nexport type ChatModelOptions = {\n /**\n * The model the chat's picker shows selected. The agent process reads the model off the chat, so\n * this is stamped onto a chat that has not selected one before its first request — otherwise the\n * picker and the process would disagree about what the conversation runs on.\n */\n model?: DXN.DXN;\n // The provider is a global setting rather than the chat's, carried so the agent process resolves\n // the (provider, id) pair — the catalog's shared model ids are ambiguous without it.\n provider?: DXN.DXN;\n modelRegistry?: Model.Registry;\n registry?: Registry.Registry;\n observableRegistry?: AtomRegistry.AtomRegistry;\n /**\n * For tracing.\n */\n chat?: Ref.Ref<Chat.Chat>;\n system?: string;\n /** Who this chat model's prompts come from, when the chat is one of several people's with one agent. */\n sender?: AgentService.PromptSender;\n /** Binds {@link TurnReviewSkill} so each turn is reviewed for struggles (opt-in setting). */\n reportStruggles?: boolean;\n};\n\nconst defaultOptions: Partial<ChatModelOptions> = {\n model: Model.DEFAULT_EDGE,\n};\n\nexport type ChatRequestOptions = {};\n\nexport type ChatRequest = {\n message: string;\n /** `synthetic` marks system-generated turn content (e.g. a completed inline flow reporting itself). */\n disposition?: ContentBlock.Text['disposition'];\n /** Ephemeral context (e.g. companion-document selection) captured at submit time. */\n context?: ChatRequestContext;\n options?: ChatRequestOptions;\n};\n\nexport type ChatSendOptions = {\n /**\n * Runs before the prompt is dispatched — after it is already showing — and a rejection fails the\n * prompt. Lets a transient chat persist and flush its conversation feed so the agent can resolve it.\n */\n prepare?: () => Promise<void> | void;\n};\n\n/** Settles the dispatch of one prompt: resolved once the agent holds it, rejected if it never will. */\nexport type PromptSubmission = { resolve: () => void; reject: (error: unknown) => void };\n\ntype OutboxPayload = { request: ChatRequest; prepare?: () => Promise<void> | void };\n\n/** User-facing message shown when an AI request is rejected for exceeding the account usage quota (HTTP 429). */\nconst QUOTA_EXCEEDED_MESSAGE = 'You have reached your AI usage limit for this period.';\n\n/** An actionable next-step a chat error surfaces in its toast; the label key is resolved by the chat UI. */\nexport type ChatErrorAction = { readonly labelKey: string };\n\n/**\n * Display error for an over-quota (HTTP 429) rejection. Declares an {@link action} so the chat toast can\n * offer a usage-dashboard link declaratively — the UI renders whatever action an error declares rather\n * than branching on the error type.\n */\nexport class AiUsageQuotaError extends Error {\n readonly action: ChatErrorAction = { labelKey: 'view-usage.label' };\n}\n\n/**\n * Matches an over-quota rejection (EDGE responds 429) in error text. The typed\n * {@link UsageQuotaExceededError} is wrapped deep in the cause chain and does not survive the\n * agent-process boundary — the failure is rendered to a string via `Cause.pretty`, which drops\n * nested causes — so detection also relies on the HTTP 429 that `@effect/ai`'s\n * {@link AiError.HttpResponseError} embeds in its message (e.g. \"... (429 POST ...\").\n */\nconst QUOTA_PATTERN = /\\b429\\b|rate.?limit|too many requests|usage quota|quota exceeded/i;\n\n/** Whether an error denotes an over-quota (HTTP 429) rejection, detected by typed status or message text. */\nconst isQuotaError = (err: unknown): boolean => {\n if (AiError.isAiError(err)) {\n // v4 classifies the failure semantically on `reason` rather than by status; these are the two\n // shapes a 429 arrives as.\n if (err.reason._tag === 'RateLimitError' || err.reason._tag === 'QuotaExhaustedError') {\n return true;\n }\n return QUOTA_PATTERN.test(describeAiError(err));\n }\n return typeof err === 'string' && QUOTA_PATTERN.test(err);\n};\n\n/** `description` is declared on only some reason variants; the wrapper's `message` is the fallback. */\nconst describeAiError = (err: AiError.AiError): string =>\n ('description' in err.reason ? err.reason.description : undefined) ?? err.message;\n\n/**\n * Matches an {@link AiModelNotAvailableError} in error text. Like the quota case, the typed error\n * does not always survive the agent-process boundary — the failure is rendered with `Cause.pretty`,\n * which drops nested causes — so detection also reads the message it stringifies to.\n */\nconst MODEL_UNAVAILABLE_PATTERN = /AI Model not available:\\s*(\\S+?):?(?=\\s|$)/i;\n\n/**\n * Matches the first line of a stringified {@link AiError.AiError} (\"…AiError: Module.method: detail\"), the\n * form a provider failure takes once it has crossed the agent-process boundary.\n */\nconst AI_ERROR_PATTERN = /AiError: [\\w$.]+: ([^\\n]+)/;\n\n/** The provider's own description of a failure that arrived stringified, anywhere in the cause chain. */\nconst stringifiedAiErrorText = (err: unknown): string | undefined => {\n const visited = new Set<unknown>();\n for (\n let current = err;\n current && !visited.has(current);\n current = current instanceof Error ? current.cause : undefined\n ) {\n visited.add(current);\n const text = typeof current === 'string' ? current : current instanceof Error ? current.message : '';\n const detail = text.match(AI_ERROR_PATTERN)?.[1]?.trim();\n if (detail) {\n return detail;\n }\n }\n return undefined;\n};\n\n/** The displayable text of a failure, which reaches the chat either typed or already stringified. */\nconst errorText = (err: unknown): string => (typeof err === 'string' ? err : err instanceof Error ? err.message : '');\n\n/** The model of a model-unavailable rejection, by typed context or message text. */\nconst unavailableModel = (err: unknown): string | undefined => {\n const typed = findInCause(err, AiModelNotAvailableError.is);\n if (typed) {\n return String(typed.context.model);\n }\n return errorText(err).match(MODEL_UNAVAILABLE_PATTERN)?.[1];\n};\n\n/**\n * Maps a failure from the agent fiber to an error suitable for display.\n * An over-quota (HTTP 429) rejection is surfaced as an actionable usage-limit message; the typed\n * {@link UsageQuotaExceededError} only survives on the direct path, so {@link isQuotaError} also\n * recognizes the stringified 429 the chat receives across the agent-process boundary.\n * A model the configured provider does not serve names that model, since the fix is to pick another\n * one in settings.\n * Other {@link AiError}s originate from the AI service and are actionable by the user\n * (e.g., \"model 'x' not found\", \"Connection refused\"), so their detail is propagated.\n * Any other failure is treated as an internal/unexpected error and reported generically\n * to avoid leaking implementation detail.\n */\nexport const parseError = (err: unknown): Error => {\n const quotaError = findInCause(err, UsageQuotaExceededError.is);\n if (quotaError || isQuotaError(err)) {\n return new AiUsageQuotaError(quotaError?.message?.trim() || QUOTA_EXCEEDED_MESSAGE, { cause: err });\n }\n\n const model = unavailableModel(err);\n if (model) {\n return new Error(`The model is not available: ${model}`, { cause: err });\n }\n\n let message: string | undefined;\n if (AiError.isAiError(err)) {\n message = describeAiError(err).trim();\n } else if (typeof err === 'string') {\n // TODO(burdon): This is brittle.\n // UnknownError: ChatCompletionsClient.streamText: model 'gemma3:27b' not found\n const [, model] = err.match(/model\\s+'([^']+)'\\s+not\\s+found/i) || [];\n if (model) {\n message = `The model is not available: ${model}`;\n }\n }\n\n if (!message) {\n message = stringifiedAiErrorText(err);\n }\n\n if (!message) {\n message = 'An unexpected error occurred.';\n }\n\n return new Error(message, { cause: err });\n};\n\n/**\n * Handles interactions with the AI service.\n * Uses AgentService to spawn a process-backed agent and subscribes to ephemeral trace events for streaming.\n */\nexport class ChatModel {\n readonly #registry: AtomRegistry.AtomRegistry;\n\n /** Pending messages (finalized, non-streaming). */\n readonly #pending = Atom.make<Message.Message[]>([]);\n\n /** Currently streaming messages (from ephemeral trace events). */\n readonly #streaming = Atom.make<Message.Message[]>([]);\n\n /** Set of message IDs that have been finalized (non-pending delivered via ephemeral). */\n readonly #finalizedIds = new Set<string>();\n\n /** Timestamp of the newest trace event applied this turn, overall and per streamed message. */\n #appliedAt = 0;\n readonly #appliedAtByMessage = new Map<string, number>();\n\n /** Currently active request fiber. */\n #requestFiber: Fiber.Fiber<void, unknown> | undefined;\n\n /** Fiber following a turn this chat model did not issue ({@link adopt}). */\n #observeFiber: Fiber.Fiber<void, unknown> | undefined;\n\n /** Last request (for retries). */\n #lastRequest: ChatRequest | undefined;\n\n /** Streaming state. */\n public readonly streaming = Atom.make<boolean>((get) => get(this.#streaming).length > 0);\n\n /** Active state. */\n public readonly active = Atom.make(false);\n\n /** Array of Messages (incl. the current message being streamed). */\n public readonly messages = Atom.make<Message.Message[]>((get) => [...get(this.#pending), ...get(this.#streaming)]);\n\n /** Last error. */\n public readonly error = Atom.make<Option.Option<Error>>(Option.none());\n\n readonly #outbox: Outbox<OutboxPayload>;\n\n /** Prompts sent from this chat, from submit onwards, in submit order (see {@link send}). */\n public readonly outbox: Atom.Atom<readonly OutboxEntry[]>;\n\n /** The feed's messages, live: what the outbox and the streamed turn are reconciled against. */\n readonly #feedMessages: Atom.Atom<readonly Message.Message[]>;\n\n /**\n * What the chat renders: the feed's turns, the turn streaming now, and the prompts on their way to\n * the agent, reconciled into one list (see `projectThread`). The UI reads this and nothing else.\n */\n public readonly thread: Atom.Atom<ThreadProjection>;\n\n /**\n * MCP server connection errors observed during the most recent request.\n * Misconfigured/unreachable servers are dropped from the toolkit so the chat\n * keeps working; the entries here let the UI display which servers failed.\n */\n public readonly mcpErrors = Atom.make<readonly Trace.PayloadType<typeof Trace.McpServerError>[]>([]);\n\n /**\n * Stage the in-flight request has reached, or `undefined` when there is nothing to report.\n *\n * Tracks the whole turn rather than only the wait before the first token: an agentic turn streams\n * a little, then calls tools for a long time, so a line cleared at the first block reads as a\n * request that has finished. Cleared when the turn settles or is cancelled.\n */\n public readonly activity = Atom.make<Trace.PayloadType<typeof Trace.RequestPhase> | undefined>(undefined);\n\n constructor(\n private readonly _conversation: AiSession.Session,\n private readonly _runtime: Capabilities.ProcessManagerRuntime,\n private readonly _feed: Feed.Feed,\n /**\n * Pre-built layer that materialises {@link SpaceServices}. Built via\n * {@link ServiceResolver.provide} with the {@link ServiceResolver} already\n * supplied (hence `RIn = never`); the {@link ServiceNotAvailableError}\n * error channel surfaces when a tag is not available for the space.\n * Provided to every effect run by the chat model so the underlying\n * {@link ProcessManagerRuntime} has access to space-affinity services.\n */\n private readonly _spaceLayer: Layer.Layer<SpaceServices, ServiceResolver.ServiceNotAvailableError, never>,\n private readonly _options: ChatModelOptions = defaultOptions,\n ) {\n this.#registry = this._options.observableRegistry ?? AtomRegistry.make();\n this.#outbox = new Outbox(this.#registry, (payload) => this.#dispatch(payload));\n this.outbox = this.#outbox.entries;\n // Held for the chat model's life: a query's atom is memoized per result, so the query must be too.\n const db = Obj.getDatabase(this._feed);\n this.#feedMessages = db\n ? db.query(Query.select(Filter.type(Message.Message)).from(this._feed)).atom\n : Atom.make<readonly Message.Message[]>([]);\n this.thread = Atom.make((get) =>\n projectThread({\n feedMessages: get(this.#feedMessages),\n pendingMessages: get(this.messages),\n rewindFrom: get(Obj.atom(this._feed)).rewindFrom,\n outbox: get(this.outbox),\n }),\n );\n if (this._options.model && !this._options.system) {\n const capabilities = this._options.modelRegistry?.getCapabilities(this._options.model) ?? {};\n this._options.system = createSystemPrompt(capabilities);\n }\n }\n\n get context(): AiContext.Binder {\n return this._conversation.context;\n }\n\n get conversation() {\n return this._conversation;\n }\n\n get registry() {\n return this._options.registry;\n }\n\n get system(): string {\n return this._options.system ?? '';\n }\n\n async getTools(): Promise<Record<string, any>> {\n return this._runtime.runPromise(\n Effect.provide(this._conversation.getTools(), ToolExecutionServices).pipe(Effect.provide(this._spaceLayer)),\n );\n }\n\n async getSystemPrompt(): Promise<string> {\n return this._runtime.runPromise(\n Effect.gen({ self: this }, function* () {\n const skills = this.context.getSkills();\n const objects = this.context.getObjects();\n const instructions = yield* this.#getInstructions();\n // Tier A only: system-prompt formatting runs operations that read the conversation context;\n // the live-host Tier B control surface is not reachable from this fiber.\n const runtime = yield* Effect.context<Database.Service>();\n return yield* formatSystemPrompt({ system: this._options.system, skills, objects, instructions }).pipe(\n Effect.provideService(\n Harness.HarnessService,\n Harness.fromBinder({ feed: this._feed, runtime, binder: this.context }),\n ),\n );\n }).pipe(Effect.provide(this._spaceLayer), Effect.orDie),\n );\n }\n\n /**\n * Resolves the chat's steering instructions, if any — the local `AiSession` used for system-prompt\n * formatting is feed-centric, so the ref is resolved here and handed down.\n */\n #getInstructions(): Effect.Effect<Instructions.Instructions[], never, Database.Service> {\n return Effect.gen({ self: this }, function* () {\n const instructionsRef = this._options.chat?.target?.instructions;\n if (!instructionsRef) {\n return [];\n }\n\n const instructions = yield* Database.load(instructionsRef).pipe(Effect.orElseSucceed(() => undefined));\n return instructions ? [instructions] : [];\n });\n }\n\n /**\n * Sends a prompt the reader typed: it joins {@link outbox} at once — the thread shows it in the\n * same frame, before anything is persisted — and is then dispatched in submit order, as a request\n * when the agent is idle and queued behind the running turn otherwise.\n *\n * Returns the outbox id, which is the prompt's thread row for its whole life.\n */\n send(request: ChatRequest, { prepare }: ChatSendOptions = {}): string {\n const content = createPromptContent(request);\n const blocks = typeof content === 'string' ? [ContentBlock.Text.make({ text: content })] : content;\n // What the feed holds now cannot be this prompt's echo, however alike it reads.\n const known = new Set(this.#registry.get(this.#feedMessages).map(({ id }) => id));\n return this.#outbox.add({ request, prepare }, { blocks, known }).id;\n }\n\n /** Forgets a prompt this client sent; withdrawing one the agent's queue holds is the feed's business. */\n removePrompt(id: string): void {\n this.#outbox.remove(id);\n }\n\n /**\n * Hands one outbox prompt to the agent, settling once it is submitted rather than when its turn\n * ends, so the next prompt can follow it into the queue while this one runs.\n */\n async #dispatch({ request, prepare }: OutboxPayload): Promise<void> {\n await prepare?.();\n // Read at dispatch, not at submit: the previous prompt's request is what makes the agent active.\n if (this.#registry.get(this.active)) {\n try {\n await this.#enqueue(request);\n } catch (err) {\n this.#registry.set(this.error, Option.some(parseError(err)));\n throw err;\n }\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n void this.request(request, { resolve, reject });\n });\n }\n\n /**\n * Initiates a new request via AgentService.\n *\n * `submission` settles once the prompt is submitted (or cannot be), which is long before the turn\n * this resolves after.\n */\n async request(requestProp: ChatRequest, submission?: PromptSubmission): Promise<void> {\n if (this.#requestFiber) {\n await this.cancel();\n }\n\n try {\n this.#lastRequest = requestProp;\n this.#registry.set(this.error, Option.none());\n this.#registry.set(this.mcpErrors, []);\n // Set locally: spawning or attaching the agent process happens before it can emit anything, and\n // that resolve is itself part of the wait the reader is watching.\n this.#registry.set(this.activity, { phase: 'starting' });\n this.#registry.set(this.active, true);\n await this.#ensureTurnReview();\n\n const effect = Effect.gen({ self: this }, function* () {\n // NOTE: Gets or creates a session for the feed.\n log.info('init agent session', {\n feed: Obj.getURI(this._feed),\n model: this._options.model,\n provider: this._options.provider,\n });\n const session = yield* this.#getSession();\n markWork('chat.session-ready');\n // Subscribed before the prompt is submitted, so nothing of this turn needs replaying, and a\n // replay would carry the process's earlier turns.\n yield* this.#forkEphemeralCollector(session, { replay: false });\n\n log('chat model submitting prompt', { length: requestProp.message.length });\n yield* session.submitPrompt(createPromptContent(requestProp), { sender: this._options.sender });\n markWork('chat.prompt-submitted');\n submission?.resolve();\n log('chat model submitPrompt returned, waiting for agent', {});\n\n // On the first message (no name yet), schedule rename immediately so it\n // runs concurrently with the AI response rather than waiting for completion. A synthetic\n // turn is system-generated, so it would name the chat after a report nobody wrote.\n if (!this._options.chat?.target?.name && requestProp.disposition !== 'synthetic') {\n yield* this.#updateChatName(requestProp.message);\n }\n\n yield* session.waitForCompletion();\n log.info('session complete');\n\n this.#flushStreaming();\n });\n\n this.#requestFiber = this._runtime.runFork(effect.pipe(Effect.provide(this._spaceLayer)));\n\n // Inspect the fiber's exit so the underlying failure (e.g. \"model 'x' not found\") is\n // preserved as a clean Error rather than an opaque FiberFailure.\n const exit = await this._runtime.runPromise(Fiber.await(this.#requestFiber));\n if (Exit.isFailure(exit)) {\n if (Cause.hasInterruptsOnly(exit.cause)) {\n // A no-op once submitted: only a prompt stopped on its way to the agent is dropped.\n submission?.reject(new PromptCancelledError());\n this.#discardStreaming();\n return;\n }\n\n throw EffectEx.causeToError(exit.cause);\n }\n\n this.#registry.set(this.error, Option.none());\n this.#lastRequest = undefined;\n this.#requestFiber = undefined;\n } catch (err) {\n // `EffectEx.causeToError` above unwraps the fiber failure into the underlying error (e.g. an AiError\n // carrying \"model 'x' not found\"); `parseError` decides what to surface to the user.\n log.error('request failed', { error: err });\n // A no-op once submitted: a turn that fails after its prompt reached the agent did deliver it.\n submission?.reject(err);\n this.#registry.set(this.error, Option.some(parseError(err)));\n } finally {\n log.info('setting active to false');\n this.#registry.set(this.active, false);\n this.#registry.set(this.activity, undefined);\n this.#requestFiber = undefined;\n }\n }\n\n /**\n * Queues a prompt behind the turn already running, instead of starting one.\n *\n * The agent's input queue is feed state, so submitting is durable and ordered: the running turn is\n * left alone and the process takes this prompt up when it settles. {@link request} cannot serve\n * this case — it cancels the in-flight turn to start its own — and the running request's\n * `waitForCompletion` already covers the queued turn, since the agent does not report completion\n * while its queue is non-empty.\n */\n async enqueue(requestProp: ChatRequest): Promise<void> {\n try {\n await this.#enqueue(requestProp);\n } catch (err) {\n log.error('enqueue failed', { error: err });\n this.#registry.set(this.error, Option.some(parseError(err)));\n }\n }\n\n async #enqueue(requestProp: ChatRequest): Promise<void> {\n await this._runtime.runPromise(\n Effect.gen({ self: this }, function* () {\n const session = yield* this.#getSession();\n yield* session.submitPrompt(createPromptContent(requestProp), { sender: this._options.sender });\n markWork('chat.prompt-submitted');\n }).pipe(Effect.provide(this._spaceLayer)),\n );\n }\n\n /**\n * Mirrors turns this chat model did not initiate into its own state: active/streaming state is\n * per-chat model ({@link useChatModel} builds one per mount) while the agent process outlives\n * the mount, so a chat remounted mid-turn would otherwise render as idle.\n *\n * Returns a disposer that stops observing.\n */\n adopt(): () => void {\n let disposed = false;\n let unsubscribe: (() => void) | undefined;\n void this._runtime\n .runPromise(this.#getSession().pipe(Effect.provide(this._spaceLayer)))\n .then((session) => {\n if (disposed) {\n return;\n }\n\n unsubscribe = this.#registry.subscribe(\n session.running,\n (running) => {\n if (running) {\n void this.#observe(session);\n }\n },\n // The turn is normally already in flight when a remounted chat gets here.\n { immediate: true },\n );\n })\n .catch((err) => log.warn('failed to attach to agent session', { error: err }));\n\n return () => {\n disposed = true;\n unsubscribe?.();\n // The collector and its stream subscription outlive the unmount otherwise — one per remount,\n // all feeding atoms nothing reads any more.\n const fiber = this.#observeFiber;\n if (fiber) {\n this.#observeFiber = undefined;\n void this._runtime.runPromise(Fiber.interrupt(fiber));\n }\n };\n }\n\n /**\n * Follows a turn started elsewhere to completion, surfacing its streamed blocks here.\n * A turn this chat model issued is owned by {@link request}, which reports its own errors.\n */\n async #observe(session: AgentService.Session): Promise<void> {\n if (this.#requestFiber || this.#observeFiber || this.#registry.get(this.active)) {\n return;\n }\n\n log.info('observing agent turn', { feed: Obj.getURI(this._feed) });\n try {\n // Attaching is the `starting` phase; the agent's next report names the real one.\n this.#registry.set(this.activity, { phase: 'starting' });\n this.#registry.set(this.active, true);\n const effect = Effect.gen({ self: this }, function* () {\n // Replayed: the turn being adopted is already under way.\n yield* this.#forkEphemeralCollector(session, { replay: true });\n yield* session.waitForCompletion();\n this.#flushStreaming();\n });\n\n // Tracked apart from `#requestFiber` so `cancel` keeps meaning \"stop the request this chat\n // issued\", and so the disposer interrupts only the observer.\n this.#observeFiber = this._runtime.runFork(effect.pipe(Effect.provide(this._spaceLayer)));\n const exit = await this._runtime.runPromise(Fiber.await(this.#observeFiber));\n if (Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)) {\n throw EffectEx.causeToError(exit.cause);\n }\n } catch (err) {\n // Reported but not surfaced as this chat's error: the mount that issued the turn owns that.\n log.warn('failed to observe agent turn', { error: err });\n } finally {\n this.#registry.set(this.active, false);\n this.#registry.set(this.activity, undefined);\n this.#observeFiber = undefined;\n }\n }\n\n /**\n * Resolves the agent session for this chat, reusing the process a previous mount left running and\n * spawning one only when there is none.\n */\n #getSession(): Effect.Effect<AgentService.Session, never, AgentService.AgentService | Database.Service> {\n return Effect.gen({ self: this }, function* () {\n const chat = this._options.chat?.target;\n if (!chat) {\n // The agent process is bound to a chat; a chat model constructed without one has no\n // conversation to run.\n return yield* Effect.die(new Error('Chat model requires a chat.'));\n }\n const selected = this._options.model;\n if (!chat.session?.model && selected) {\n Obj.update(chat, (chat) => {\n chat.session = { ...chat.session, model: selected };\n });\n }\n // The model is the chat's, so the provider has to be the one that serves THAT model: the\n // configured provider can have moved on since the chat made its selection.\n const model = chat.session?.model ?? selected;\n return yield* AgentService.getSession(chat, {\n provider: model ? providerForModel(model, this._options.provider) : this._options.provider,\n location: Agent.chatLocation(chat),\n });\n });\n }\n\n /**\n * Forks the collector for the session's ephemeral trace events (streaming blocks and MCP\n * failures) as a child of the calling fiber.\n */\n #forkEphemeralCollector(\n session: AgentService.Session,\n options: Process.SubscribeEphemeralOptions,\n ): Effect.Effect<void> {\n return session.subscribeEphemeral(options).pipe(\n Stream.runForEach((message) =>\n Effect.sync(() => {\n for (const event of message.events) {\n // A remote agent's trace is relayed over the swarm, which does not preserve order, so a\n // phase or partial older than one already applied is stale rather than news.\n if (Trace.isOfType(Trace.PartialBlock, event)) {\n // Read before `#isStale` records it: a block can be news for its message yet older than a\n // phase already shown, which it must not turn back into `generating`.\n const current = event.timestamp >= this.#appliedAt;\n if (this.#isStale(event.timestamp, event.data.messageId)) {\n continue;\n }\n this.#handleEphemeralMessage(event.data, current);\n } else if (Trace.isOfType(Trace.RequestPhase, event)) {\n if (this.#isStale(event.timestamp)) {\n continue;\n }\n this.#registry.set(this.activity, event.data);\n } else if (Trace.isOfType(Trace.McpServerError, event)) {\n this.#handleMcpError(event.data);\n }\n }\n }),\n ),\n Effect.forkChild,\n Effect.asVoid,\n );\n }\n\n /**\n * Cancels the current request.\n */\n async cancel(): Promise<void> {\n await EffectEx.runAndForwardErrors(\n Effect.gen({ self: this }, function* () {\n log.info('cancelling request', { fiber: this.#requestFiber });\n if (this.#requestFiber) {\n yield* Fiber.interrupt(this.#requestFiber);\n }\n // Same options as `request`: looked up bare, a differing model/provider/instructions reads as\n // a reconfiguration, which tears down the running process and spawns a replacement purely to\n // terminate it again.\n const session = yield* this.#getSession();\n yield* session.terminate();\n }).pipe(Effect.provide(this._spaceLayer)),\n );\n\n this.#requestFiber = undefined;\n this.#discardStreaming();\n this.#registry.set(this.active, false);\n }\n\n /**\n * Retry last failed request.\n */\n async retry(): Promise<void> {\n if (this.#lastRequest) {\n return this.request(this.#lastRequest);\n }\n }\n\n /**\n * Clears the recorded MCP server errors (e.g. after the user dismisses the warning banner).\n */\n dismissMcpErrors(): void {\n this.#registry.set(this.mcpErrors, []);\n }\n\n /**\n * Update the current chat's name.\n */\n async updateName(chat: Chat.Chat): Promise<void> {\n const spaceId = Obj.getDatabase(chat)?.spaceId;\n if (!spaceId) {\n return;\n }\n EffectEx.unwrapExit(\n await this._runtime.runPromiseExit(\n Operation.invoke(AssistantOperation.UpdateChatName, { chat }, { spaceId }).pipe(\n Effect.provide(this._spaceLayer),\n ),\n ),\n );\n }\n\n /**\n * Whether a trace event is older than one already applied (per message for a streamed block, whose\n * text is the whole reply so far), recording it as the newest when it is not.\n */\n #isStale(timestamp: number, messageId?: string): boolean {\n const appliedAt = messageId === undefined ? this.#appliedAt : (this.#appliedAtByMessage.get(messageId) ?? 0);\n if (timestamp < appliedAt) {\n return true;\n }\n this.#appliedAt = Math.max(this.#appliedAt, timestamp);\n if (messageId !== undefined) {\n this.#appliedAtByMessage.set(messageId, timestamp);\n }\n return false;\n }\n\n /**\n * Handles an ephemeral message from the agent process.\n * Both pending and completed blocks arrive here. Completed blocks are deduped\n * against messages already written to the feed queue to handle the race between\n * ephemeral delivery and feed replication.\n */\n #handleEphemeralMessage(event: Trace.PayloadType<typeof Trace.PartialBlock>, current = true) {\n // Content arriving is what \"generating\" means, and deriving it here keeps it out of the agent's\n // streaming pipeline, where the extra yield a trace write costs is observable to the turn's\n // tools. A tool call the agent reports supersedes it for as long as the tool runs.\n if (current) {\n this.#registry.set(this.activity, { phase: 'generating' });\n }\n\n const isPending = event.block.pending;\n const message = Obj.make(Message.Message, {\n id: event.messageId,\n created: new Date().toISOString(),\n sender: { role: event.role },\n blocks: [event.block],\n });\n\n if (isPending) {\n if (this.#finalizedIds.has(event.messageId)) {\n return;\n }\n this.#registry.update(this.#streaming, (streaming) => {\n const idx = streaming.findIndex((existing) => existing.id === event.messageId);\n if (idx >= 0) {\n const updated = [...streaming];\n updated[idx] = message;\n return updated;\n }\n return [...streaming, message];\n });\n } else {\n this.#finalizedIds.add(event.messageId);\n this.#registry.update(this.#streaming, (streaming) => streaming.filter((existing) => existing.id !== message.id));\n this.#registry.update(this.#pending, (pending) => {\n if (pending.some((existing) => existing.id === message.id)) {\n return pending;\n }\n return [...pending, message];\n });\n }\n }\n\n /**\n * Records a per-server MCP failure, deduped by url+protocol so repeat misconfigurations\n * across turns do not spam the UI.\n */\n #handleMcpError(event: Trace.PayloadType<typeof Trace.McpServerError>) {\n log.warn('MCP server error', event);\n this.#registry.update(this.mcpErrors, (errors) => {\n if (errors.some((existing) => existing.url === event.url && existing.protocol === event.protocol)) {\n return errors;\n }\n return [...errors, event];\n });\n }\n\n /**\n * Drop in-flight streaming messages (cancel/interrupt — partial blocks are discarded).\n */\n #discardStreaming() {\n this.#registry.set(this.#streaming, []);\n this.#registry.set(this.activity, undefined);\n this.#finalizedIds.clear();\n this.#resetApplied();\n }\n\n /**\n * Move remaining streaming messages to pending (called when agent completes).\n */\n #flushStreaming() {\n // The activity line is left to the settle that clears `active`: cleared here, it would vanish a tick\n // before the turn reads as finished.\n const remaining = this.#registry.get(this.#streaming);\n if (remaining.length > 0) {\n this.#registry.update(this.#pending, (pending) => [...pending, ...remaining]);\n this.#registry.set(this.#streaming, []);\n }\n this.#finalizedIds.clear();\n this.#resetApplied();\n }\n\n #resetApplied() {\n this.#appliedAt = 0;\n this.#appliedAtByMessage.clear();\n }\n\n /**\n * Binds the turn-review skill before a request while the user is opted in; its background\n * end-request hook does the review, so nothing here waits on it. Left bound after an opt-out,\n * since the hook re-checks the setting.\n */\n async #ensureTurnReview(): Promise<void> {\n if (!this._options.reportStruggles) {\n return;\n }\n const bound = this.context.getSkills().some((skill) => Obj.getMeta(skill).key === TurnReviewSkill.key);\n if (!bound) {\n await this.context.bind({ skills: [Ref.fromURI(Skill.registryURI(TurnReviewSkill.key))] });\n }\n }\n\n /**\n * Schedule a chat name update as a detached (fire-and-forget) operation.\n * Called automatically on the first message; can also be invoked manually via the toolbar.\n */\n #updateChatName(prompt?: string): Effect.Effect<void, never, Operation.Service> {\n const chat = this._options.chat?.target;\n if (!chat) {\n return Effect.void;\n }\n\n const spaceId = Obj.getDatabase(chat)?.spaceId;\n if (!spaceId) {\n return Effect.void;\n }\n\n log.info('scheduling chat name update');\n return Operation.schedule(AssistantOperation.UpdateChatName, { chat, prompt }, { spaceId });\n }\n}\n\nexport type ChatModelState = Pick<ChatModel, 'streaming' | 'active' | 'thread' | 'error' | 'mcpErrors' | 'activity'>;\n\nconst idleChatModelState: ChatModelState = {\n streaming: Atom.make(false),\n active: Atom.make(false),\n thread: Atom.make<ThreadProjection>(EMPTY_THREAD),\n error: Atom.make<Option.Option<Error>>(Option.none()),\n mcpErrors: Atom.make<readonly Trace.PayloadType<typeof Trace.McpServerError>[]>([]),\n activity: Atom.make<Trace.PayloadType<typeof Trace.RequestPhase> | undefined>(undefined),\n};\n\nexport const getChatModelState = (chatModel: ChatModel | undefined): ChatModelState => chatModel ?? idleChatModelState;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-context.mjs","names":[],"sources":["../../src/components/Chat/context.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Alarm } from '@dxos/assistant';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport { type Event } from '@dxos/async';\nimport { type Database } from '@dxos/echo';\nimport { createContext } from '@dxos/react-hooks';\nimport { type ChatThreadController } from '@dxos/react-ui-assistant';\nimport { type MessageRange } from '@dxos/react-ui-feed';\nimport { type Message } from '@dxos/types';\n\nimport { type
|
|
1
|
+
{"version":3,"file":"chunk-context.mjs","names":[],"sources":["../../src/components/Chat/context.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Alarm } from '@dxos/assistant';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport { type Event } from '@dxos/async';\nimport { type Database } from '@dxos/echo';\nimport { createContext } from '@dxos/react-hooks';\nimport { type ChatThreadController } from '@dxos/react-ui-assistant';\nimport { type MessageRange } from '@dxos/react-ui-feed';\nimport { type Message } from '@dxos/types';\n\nimport { type ChatModel } from '../../chat-model/index.ts';\nimport { type ChatEvent } from './events.ts';\n\n/**\n * Wall-clock timestamps for the most-recent (or in-flight) request, lifted out of\n * `ChatStreamStatus` so the elapsed value survives across re-mounts triggered when wire's\n * drip queue toggles `wireDrainingEffect` (which removes/restores the footer block widget).\n * `endedAt` is `null` while the request is still active.\n */\nexport type ChatRequestTiming = {\n startedAt: number;\n endedAt: number | null;\n};\n\n/**\n * What the chat's parts act through: handles that hold still for the life of the chat. Kept apart\n * from {@link ChatThreadContextValue}, which changes with every streamed block — a consumer\n * re-renders whenever its context does, so the toolbar, the composer and the checklist re-rendered\n * per block of every turn when the two were one context.\n */\nexport type ChatContextValue = {\n debug?: boolean;\n event: Event<ChatEvent>;\n db?: Database.Database;\n chat?: Chat.Chat;\n /** Undefined while the chat model is still opening; the chat renders from the feed meanwhile. */\n chatModel?: ChatModel;\n /** Whether the conversation has begun; a boolean, so it changes once rather than per message. */\n started: boolean;\n /** How many prompts wait behind the running turn; a count, so it changes per enqueue rather than per block. */\n queueSize: number;\n setController: (controller: ChatThreadController | null) => void;\n setVisibleRange: (range: MessageRange | undefined) => void;\n};\n\n/** What the chat currently shows: the projected thread and the state that moves with it. */\nexport type ChatThreadContextValue = {\n messages: Message.Message[];\n /** How many rows at the end of `messages` are prompts the agent has not taken up yet. */\n tail: number;\n /** Alarms still waiting to fire, earliest first. */\n alarms: Alarm.Alarm[];\n /** Alarms that have woken the agent since the last user prompt. */\n selfWakes: number;\n requestTiming: ChatRequestTiming | null;\n /** The thread's controller, shared between `Chat.Thread` and `Chat.Outline`. */\n controller: ChatThreadController | null;\n /** The visible index range, published by `Chat.Thread` as the reader scrolls. */\n visibleRange?: MessageRange;\n};\n\n// Internal: not re-exported from `Chat/index.ts`. Accessed by sibling components in this\n// package (e.g. `ChatStreamStatus`) without dragging in `Chat.tsx`'s heavy transitive\n// imports (transcription, etc.).\nexport const [ChatContextProvider, useChatContext] = createContext<ChatContextValue>('Chat');\n\nexport const [ChatThreadContextProvider, useChatThreadContext] = createContext<ChatThreadContextValue>('ChatThread');\n\n/**\n * Report path for agent-requested surfaces rendered inside the thread (`<surface>` blocks): a\n * completed inline flow (a connector authorized, a plugin enabled) has to reach the agent, which\n * otherwise waits on a click it never observes. `submit` posts the report as an ordinary user turn.\n */\nexport type ChatReportContextValue = {\n submit: (text: string) => void;\n};\n\n// Defaulted (unlike `ChatContext`) so a surface rendered outside a chat — a storybook, a standalone\n// preview — drops its report instead of throwing.\nexport const [ChatReportContextProvider, useChatReportContext] = createContext<ChatReportContextValue>('ChatReport', {\n submit: () => {},\n});\n"],"mappings":";;AAmEA,IAAa,CAAC,qBAAqB,kBAAkB,cAAgC,MAAM;AAE3F,IAAa,CAAC,2BAA2B,wBAAwB,cAAsC,YAAY;AAanH,IAAa,CAAC,2BAA2B,wBAAwB,cAAsC,cAAc,EACnH,cAAc,CAAC,EACjB,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as contributesPluginManager, t as bindChatDefaults } from "./chunk-default-skills.mjs";
|
|
1
2
|
import * as Capability from "@dxos/app-framework/Capability";
|
|
2
3
|
import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
|
|
3
4
|
import { AssistantOperation } from "#types";
|
|
@@ -7,12 +8,6 @@ import { Database, Feed, Ref } from "@dxos/echo";
|
|
|
7
8
|
import { AiContext } from "@dxos/assistant";
|
|
8
9
|
import * as Effect from "effect/Effect";
|
|
9
10
|
import * as Operation from "@dxos/compute/Operation";
|
|
10
|
-
import * as Skill from "@dxos/compute/Skill";
|
|
11
|
-
import { AssistantSkill, PluginManagerSkill } from "#skills";
|
|
12
|
-
import * as AlarmSkill from "@dxos/assistant-toolkit/AlarmSkill";
|
|
13
|
-
import * as ChatContextSkill from "@dxos/assistant-toolkit/ChatContextSkill";
|
|
14
|
-
import * as PlanningSkill from "@dxos/assistant-toolkit/PlanningSkill";
|
|
15
|
-
import * as DatabaseSkill from "@dxos/plugin-space/DatabaseSkill";
|
|
16
11
|
//#region src/operations/create-chat.ts
|
|
17
12
|
var handler = AssistantOperation.CreateChat.pipe(Operation.withHandler(Effect.fnUntraced(function* ({ name, instructions }) {
|
|
18
13
|
const registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
@@ -23,25 +18,16 @@ var handler = AssistantOperation.CreateChat.pipe(Operation.withHandler(Effect.fn
|
|
|
23
18
|
feed: Ref.make(feed),
|
|
24
19
|
instructions
|
|
25
20
|
});
|
|
26
|
-
const
|
|
27
|
-
const pluginManagerContributed = (yield* Capability.getAll(AppCapabilities.SkillDefinition)).some(({ key }) => key === PluginManagerSkill.key);
|
|
21
|
+
const pluginManager = contributesPluginManager(yield* Capability.getAll(AppCapabilities.SkillDefinition));
|
|
28
22
|
const runtime = yield* Effect.context();
|
|
29
23
|
const binder = new AiContext.Binder({
|
|
30
24
|
feed,
|
|
31
25
|
runtime,
|
|
32
26
|
registry
|
|
33
27
|
});
|
|
34
|
-
yield* Effect.promise(() => binder.use((b) => b
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
DatabaseSkill,
|
|
38
|
-
ChatContextSkill,
|
|
39
|
-
SkillManagerSkill,
|
|
40
|
-
AlarmSkill,
|
|
41
|
-
PlanningSkill,
|
|
42
|
-
...pluginManagerContributed ? [PluginManagerSkill] : []
|
|
43
|
-
].map(({ key }) => Ref.fromURI(Skill.registryURI(key))),
|
|
44
|
-
objects: [Ref.make(chat)]
|
|
28
|
+
yield* Effect.promise(() => binder.use((b) => bindChatDefaults(b, {
|
|
29
|
+
chat,
|
|
30
|
+
pluginManager
|
|
45
31
|
})));
|
|
46
32
|
return { object: chat };
|
|
47
33
|
})));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-create-chat.mjs","names":[],"sources":["../../src/operations/create-chat.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport { AiContext } from '@dxos/assistant';\nimport * as
|
|
1
|
+
{"version":3,"file":"chunk-create-chat.mjs","names":[],"sources":["../../src/operations/create-chat.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport { AiContext } from '@dxos/assistant';\nimport * as Chat from '@dxos/assistant/Chat';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Database, Feed, Ref } from '@dxos/echo';\n\nimport { AssistantOperation } from '#types';\n\nimport { bindChatDefaults, contributesPluginManager } from '../util/default-skills.ts';\n\nconst handler: Operation.WithHandler<typeof AssistantOperation.CreateChat> = AssistantOperation.CreateChat.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* ({ name, instructions }) {\n const registry = yield* Capability.get(Capabilities.AtomRegistry);\n const { db } = yield* Database.Service;\n\n // The chat is left for the caller to add (`SpaceOperation.AddObject`); the feed is added here only\n // because the default bindings below are written immediately, and `Feed.query` asserts a stored feed.\n // TODO(wittjosiah): Defer binding until the caller has added the chat, so the feed can stay in\n // memory too — nothing needs to write to a feed before its chat is in the database.\n const feed = db.add(Feed.make());\n const chat = Chat.make({ name, feed: Ref.make(feed), instructions });\n\n const pluginManager = contributesPluginManager(yield* Capability.getAll(AppCapabilities.SkillDefinition));\n\n const runtime = yield* Effect.context<Database.Service>();\n const binder = new AiContext.Binder({ feed, runtime, registry });\n yield* Effect.promise(() => binder.use((b: AiContext.Binder) => bindChatDefaults(b, { chat, pluginManager })));\n\n return { object: chat };\n }),\n ),\n);\n\nexport default handler;\n"],"mappings":";;;;;;;;;;;AAkBA,IAAM,UAAuE,mBAAmB,WAAW,KACzG,UAAU,YACR,OAAO,WAAW,WAAW,EAAE,MAAM,gBAAgB;CACnD,MAAM,WAAW,OAAO,WAAW,IAAI,aAAa,YAAY;CAChE,MAAM,EAAE,OAAO,OAAO,SAAS;CAM/B,MAAM,OAAO,GAAG,IAAI,KAAK,KAAK,CAAC;CAC/B,MAAM,OAAO,KAAK,KAAK;EAAE;EAAM,MAAM,IAAI,KAAK,IAAI;EAAG;CAAa,CAAC;CAEnE,MAAM,gBAAgB,yBAAyB,OAAO,WAAW,OAAO,gBAAgB,eAAe,CAAC;CAExG,MAAM,UAAU,OAAO,OAAO,QAA0B;CACxD,MAAM,SAAS,IAAI,UAAU,OAAO;EAAE;EAAM;EAAS;CAAS,CAAC;CAC/D,OAAO,OAAO,cAAc,OAAO,KAAK,MAAwB,iBAAiB,GAAG;EAAE;EAAM;CAAc,CAAC,CAAC,CAAC;CAE7G,OAAO,EAAE,QAAQ,KAAK;AACxB,CAAC,CACH,CACF"}
|
|
@@ -5,12 +5,12 @@ import * as Chat from "@dxos/assistant/Chat";
|
|
|
5
5
|
import { Database, Obj, Type } from "@dxos/echo";
|
|
6
6
|
import { isTruthy } from "@dxos/util";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Agent from "@dxos/assistant/Agent";
|
|
8
9
|
import * as Operation from "@dxos/compute/Operation";
|
|
9
10
|
import * as Skill from "@dxos/compute/Skill";
|
|
10
11
|
import * as Layer from "effect/Layer";
|
|
11
12
|
import * as ServiceResolver from "@dxos/compute/ServiceResolver";
|
|
12
13
|
import { Sequence } from "@dxos/conductor";
|
|
13
|
-
import * as Agent from "@dxos/assistant/Agent";
|
|
14
14
|
import * as SpaceOperation from "@dxos/plugin-space/SpaceOperation";
|
|
15
15
|
import * as AgentSkill from "@dxos/assistant-toolkit/AgentSkill";
|
|
16
16
|
import * as SpaceCapabilities from "@dxos/plugin-space/SpaceCapabilities";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Ref } from "@dxos/echo";
|
|
2
|
+
import * as Skill from "@dxos/compute/Skill";
|
|
3
|
+
import { AssistantSkill, PluginManagerSkill } from "#skills";
|
|
4
|
+
import * as AlarmSkill from "@dxos/assistant-toolkit/AlarmSkill";
|
|
5
|
+
import * as ChatContextSkill from "@dxos/assistant-toolkit/ChatContextSkill";
|
|
6
|
+
import * as PlanningSkill from "@dxos/assistant-toolkit/PlanningSkill";
|
|
7
|
+
import * as SkillManagerSkill from "@dxos/assistant-toolkit/SkillManagerSkill";
|
|
8
|
+
import * as DatabaseSkill from "@dxos/plugin-space/DatabaseSkill";
|
|
9
|
+
//#region src/util/default-skills.ts
|
|
10
|
+
/** Only an extensible host contributes the plugin-manager skill; elsewhere its tools cannot run. */
|
|
11
|
+
var contributesPluginManager = (contributed) => contributed.some(({ key }) => key === PluginManagerSkill.key);
|
|
12
|
+
var defaultChatSkills = (pluginManager) => [
|
|
13
|
+
AssistantSkill,
|
|
14
|
+
DatabaseSkill,
|
|
15
|
+
ChatContextSkill,
|
|
16
|
+
SkillManagerSkill,
|
|
17
|
+
AlarmSkill,
|
|
18
|
+
PlanningSkill,
|
|
19
|
+
...pluginManager ? [PluginManagerSkill] : []
|
|
20
|
+
].map(({ key }) => Ref.fromURI(Skill.registryURI(key)));
|
|
21
|
+
var bindChatDefaults = (binder, { chat, pluginManager }) => binder.bind({
|
|
22
|
+
skills: defaultChatSkills(pluginManager),
|
|
23
|
+
objects: [Ref.make(chat)]
|
|
24
|
+
});
|
|
25
|
+
//#endregion
|
|
26
|
+
export { contributesPluginManager as n, bindChatDefaults as t };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=chunk-default-skills.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-default-skills.mjs","names":[],"sources":["../../src/util/default-skills.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type AiContext } from '@dxos/assistant';\nimport * as AlarmSkill from '@dxos/assistant-toolkit/AlarmSkill';\nimport * as ChatContextSkill from '@dxos/assistant-toolkit/ChatContextSkill';\nimport * as PlanningSkill from '@dxos/assistant-toolkit/PlanningSkill';\nimport * as SkillManagerSkill from '@dxos/assistant-toolkit/SkillManagerSkill';\nimport type * as Chat from '@dxos/assistant/Chat';\nimport * as Skill from '@dxos/compute/Skill';\nimport { Ref } from '@dxos/echo';\nimport * as DatabaseSkill from '@dxos/plugin-space/DatabaseSkill';\n\nimport { AssistantSkill, PluginManagerSkill } from '#skills';\n\n/** Only an extensible host contributes the plugin-manager skill; elsewhere its tools cannot run. */\nexport const contributesPluginManager = (contributed: readonly Pick<Skill.Definition, 'key'>[]): boolean =>\n contributed.some(({ key }) => key === PluginManagerSkill.key);\n\nconst defaultChatSkills = (pluginManager: boolean): Ref.Ref<Skill.Skill>[] =>\n [\n AssistantSkill,\n DatabaseSkill,\n ChatContextSkill,\n SkillManagerSkill,\n AlarmSkill,\n PlanningSkill,\n ...(pluginManager ? [PluginManagerSkill] : []),\n ].map(({ key }) => Ref.fromURI(Skill.registryURI(key)));\n\nexport const bindChatDefaults = (\n binder: AiContext.Binder,\n { chat, pluginManager }: { chat: Chat.Chat; pluginManager: boolean },\n): Promise<void> => binder.bind({ skills: defaultChatSkills(pluginManager), objects: [Ref.make(chat)] });\n"],"mappings":";;;;;;;;;;AAiBA,IAAa,4BAA4B,gBACvC,YAAY,MAAM,EAAE,UAAU,QAAQ,mBAAmB,GAAG;AAE9D,IAAM,qBAAqB,kBACzB;CACE;CACA;CACA;CACA;CACA;CACA;CACA,GAAI,gBAAgB,CAAC,kBAAkB,IAAI,CAAC;AAC9C,CAAC,CAAC,KAAK,EAAE,UAAU,IAAI,QAAQ,MAAM,YAAY,GAAG,CAAC,CAAC;AAExD,IAAa,oBACX,QACA,EAAE,MAAM,oBACU,OAAO,KAAK;CAAE,QAAQ,kBAAkB,aAAa;CAAG,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC;AAAE,CAAC"}
|
|
@@ -7,8 +7,9 @@ import { AiService } from "@dxos/ai";
|
|
|
7
7
|
import { log } from "@dxos/log";
|
|
8
8
|
import * as Effect from "effect/Effect";
|
|
9
9
|
import * as Operation from "@dxos/compute/Operation";
|
|
10
|
-
import * as
|
|
10
|
+
import * as Array from "effect/Array";
|
|
11
11
|
import * as Schema from "effect/Schema";
|
|
12
|
+
import * as TypeOptions from "@dxos/app-toolkit/TypeOptions";
|
|
12
13
|
import * as LanguageModel from "effect/ai/LanguageModel";
|
|
13
14
|
//#region src/operations/generate-home-suggestions.ts
|
|
14
15
|
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-assistant/src/operations/generate-home-suggestions.ts";
|
|
@@ -28,9 +29,12 @@ var ONBOARDING_DOCUMENT_LABELS = /* @__PURE__ */ new Set(["Welcome to Composer"]
|
|
|
28
29
|
var handler = AssistantOperation.GenerateHomeSuggestions.pipe(Operation.withHandler(Effect.fnUntraced(function* () {
|
|
29
30
|
const { db } = yield* Database.Service;
|
|
30
31
|
const spaceId = db.spaceId;
|
|
32
|
+
const entry = (yield* Capabilities.getAtomValue(AssistantCapabilities.HomeSuggestionsCache))[spaceId];
|
|
33
|
+
const age = entry ? Date.now() - entry.generatedAt : Infinity;
|
|
34
|
+
if (entry && age < MIN_REFRESH_MS) return { prompts: [...entry.prompts] };
|
|
31
35
|
const schemas = yield* Capability.getAll(AppCapabilities.Schema);
|
|
32
36
|
const collectionTypename = Type.getTypename(Collection.Collection);
|
|
33
|
-
const types = schemas.flat().filter(Type.isType).filter((type) => TypeOptions.isUserType(type)).filter((type) => Type.getTypename(type) !== collectionTypename);
|
|
37
|
+
const types = Array.dedupeWith(schemas.flat().filter(Type.isType).filter((type) => TypeOptions.isUserType(type)).filter((type) => Type.getTypename(type) !== collectionTypename), (a, b) => Type.getURI(a) === Type.getURI(b));
|
|
34
38
|
if (types.length === 0) return { prompts: [] };
|
|
35
39
|
const items = (yield* Effect.promise(() => db.query(Query.select(Filter.or(...types.map((type) => Filter.type(type)))).orderBy(Order.updated("desc")).limit(RECENT_LIMIT)).run())).filter(Obj.isObject).filter((obj) => !Obj.isDeleted(obj)).filter((obj) => !ONBOARDING_DOCUMENT_LABELS.has(Obj.getLabel(obj) ?? "")).flatMap((obj) => {
|
|
36
40
|
const label = Obj.getLabel(obj);
|
|
@@ -42,9 +46,7 @@ var handler = AssistantOperation.GenerateHomeSuggestions.pipe(Operation.withHand
|
|
|
42
46
|
});
|
|
43
47
|
if (items.length < MIN_RECENT_OBJECTS) return { prompts: [] };
|
|
44
48
|
const fingerprint = items.map(({ label, typename }) => `${typename}:${label}`).sort().join("\n");
|
|
45
|
-
|
|
46
|
-
const age = entry ? Date.now() - entry.generatedAt : Infinity;
|
|
47
|
-
if (entry && (age < MIN_REFRESH_MS || entry.fingerprint === fingerprint && age < CACHE_TTL_MS)) return { prompts: [...entry.prompts] };
|
|
49
|
+
if (entry && entry.fingerprint === fingerprint && age < CACHE_TTL_MS) return { prompts: [...entry.prompts] };
|
|
48
50
|
const validPrompts = (yield* generateSuggestions(items)).map((prompt) => prompt.trim()).filter((prompt) => prompt.length > 0);
|
|
49
51
|
if (validPrompts.length > 0) yield* Capabilities.updateAtomValue(AssistantCapabilities.HomeSuggestionsCache, (current) => ({
|
|
50
52
|
...current,
|
|
@@ -69,7 +71,7 @@ var generateSuggestions = (items) => Effect.scoped(LanguageModel.generateObject(
|
|
|
69
71
|
log.warn("generate-home-suggestions: LLM call failed", { err }, {
|
|
70
72
|
"~LogMeta": "~LogMeta",
|
|
71
73
|
F: __dxlog_file,
|
|
72
|
-
L:
|
|
74
|
+
L: 133,
|
|
73
75
|
S: void 0
|
|
74
76
|
});
|
|
75
77
|
return Effect.succeed([]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-generate-home-suggestions.mjs","names":[],"sources":["../../src/operations/generate-home-suggestions.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as LanguageModel from 'effect/ai/LanguageModel';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { AiService } from '@dxos/ai';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as TypeOptions from '@dxos/app-toolkit/TypeOptions';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Collection, Database, Filter, Obj, Order, Query, Type } from '@dxos/echo';\nimport { log } from '@dxos/log';\n\nimport { AssistantCapabilities, AssistantOperation } from '#types';\n\nconst MODEL = 'com.anthropic.model.claude-haiku-4-5.default';\nconst RECENT_LIMIT = 20;\nconst MAX_PROMPTS = 3;\n\n/**\n * Fewer recent objects than this get the fallback prompts: a near-empty space gives the model too\n * little to personalize from, and it is the state every new identity (and every e2e run) starts in.\n */\nconst MIN_RECENT_OBJECTS = 5;\n\n/** Prompts generated from an unchanged set of recent objects are reused for this long. */\nconst CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\n/** Prompts are reused this long even after the set changes, so an active session regenerates at most hourly. */\nconst MIN_REFRESH_MS = 60 * 60 * 1000;\n\n// Onboarding documents added by the system at identity creation — exclude them so a\n// brand-new default space (containing only the welcome doc) still uses fallback prompts.\nconst ONBOARDING_DOCUMENT_LABELS = new Set(['Welcome to Composer']);\n\nconst handler: Operation.WithHandler<typeof AssistantOperation.GenerateHomeSuggestions> =\n AssistantOperation.GenerateHomeSuggestions.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* () {\n const { db } = yield* Database.Service;\n const spaceId = db.spaceId;\n\n // Build the recent-objects filter (mirrors SpaceHomeRecent).\n const schemas = yield* Capability.getAll(AppCapabilities.Schema);\n const collectionTypename = Type.getTypename(Collection.Collection);\n const types = schemas\n
|
|
1
|
+
{"version":3,"file":"chunk-generate-home-suggestions.mjs","names":[],"sources":["../../src/operations/generate-home-suggestions.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as LanguageModel from 'effect/ai/LanguageModel';\nimport * as Array from 'effect/Array';\nimport * as Effect from 'effect/Effect';\nimport * as Schema from 'effect/Schema';\n\nimport { AiService } from '@dxos/ai';\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as TypeOptions from '@dxos/app-toolkit/TypeOptions';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Collection, Database, Filter, Obj, Order, Query, Type } from '@dxos/echo';\nimport { log } from '@dxos/log';\n\nimport { AssistantCapabilities, AssistantOperation } from '#types';\n\nconst MODEL = 'com.anthropic.model.claude-haiku-4-5.default';\nconst RECENT_LIMIT = 20;\nconst MAX_PROMPTS = 3;\n\n/**\n * Fewer recent objects than this get the fallback prompts: a near-empty space gives the model too\n * little to personalize from, and it is the state every new identity (and every e2e run) starts in.\n */\nconst MIN_RECENT_OBJECTS = 5;\n\n/** Prompts generated from an unchanged set of recent objects are reused for this long. */\nconst CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000;\n\n/** Prompts are reused this long even after the set changes, so an active session regenerates at most hourly. */\nconst MIN_REFRESH_MS = 60 * 60 * 1000;\n\n// Onboarding documents added by the system at identity creation — exclude them so a\n// brand-new default space (containing only the welcome doc) still uses fallback prompts.\nconst ONBOARDING_DOCUMENT_LABELS = new Set(['Welcome to Composer']);\n\nconst handler: Operation.WithHandler<typeof AssistantOperation.GenerateHomeSuggestions> =\n AssistantOperation.GenerateHomeSuggestions.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* () {\n const { db } = yield* Database.Service;\n const spaceId = db.spaceId;\n\n const cache = yield* Capabilities.getAtomValue(AssistantCapabilities.HomeSuggestionsCache);\n const entry = cache[spaceId];\n const age = entry ? Date.now() - entry.generatedAt : Infinity;\n if (entry && age < MIN_REFRESH_MS) {\n return { prompts: [...entry.prompts] };\n }\n\n // Build the recent-objects filter (mirrors SpaceHomeRecent).\n const schemas = yield* Capability.getAll(AppCapabilities.Schema);\n const collectionTypename = Type.getTypename(Collection.Collection);\n const types = Array.dedupeWith(\n schemas\n .flat()\n .filter(Type.isType)\n .filter((type) => TypeOptions.isUserType(type))\n .filter((type) => Type.getTypename(type) !== collectionTypename),\n (a, b) => Type.getURI(a) === Type.getURI(b),\n );\n if (types.length === 0) {\n return { prompts: [] };\n }\n\n const objects = yield* Effect.promise(() =>\n db\n .query(\n Query.select(Filter.or(...types.map((type) => Filter.type(type))))\n .orderBy(Order.updated('desc'))\n .limit(RECENT_LIMIT),\n )\n .run(),\n );\n\n const items = objects\n .filter(Obj.isObject)\n .filter((obj) => !Obj.isDeleted(obj))\n .filter((obj) => !ONBOARDING_DOCUMENT_LABELS.has(Obj.getLabel(obj) ?? ''))\n .flatMap((obj): { label: string; typename: string }[] => {\n const label = Obj.getLabel(obj);\n const typename = Obj.getTypename(obj);\n return label && typename ? [{ label, typename }] : [];\n });\n if (items.length < MIN_RECENT_OBJECTS) {\n return { prompts: [] };\n }\n\n // Keyed on the set rather than the order: editing an object reorders the recent list without\n // changing what the prompts are about.\n const fingerprint = items\n .map(({ label, typename }) => `${typename}:${label}`)\n .sort()\n .join('\\n');\n if (entry && entry.fingerprint === fingerprint && age < CACHE_TTL_MS) {\n return { prompts: [...entry.prompts] };\n }\n\n const prompts = yield* generateSuggestions(items);\n const validPrompts = prompts.map((prompt) => prompt.trim()).filter((prompt) => prompt.length > 0);\n\n if (validPrompts.length > 0) {\n yield* Capabilities.updateAtomValue(AssistantCapabilities.HomeSuggestionsCache, (current) => ({\n ...current,\n [spaceId]: { generatedAt: Date.now(), prompts: validPrompts, fingerprint },\n }));\n }\n\n return { prompts: validPrompts };\n }),\n ),\n );\n\nconst generateSuggestions = (items: { label: string; typename: string }[]) =>\n Effect.scoped(\n LanguageModel.generateObject({\n schema: Schema.Struct({ prompts: Schema.Array(Schema.String) }),\n prompt: [\n 'Generate exactly 3 short, imperative, specific starter prompts for an AI assistant with access to this workspace.',\n 'Each prompt must be one sentence, at most 10 words, with no markdown or numbering.',\n '',\n 'Recent workspace objects:',\n ...items.map(({ label, typename }) => `- ${label} (${typename})`),\n ].join('\\n'),\n }),\n ).pipe(\n Effect.map(({ value }) => [...value.prompts.slice(0, MAX_PROMPTS)]),\n Effect.catch((err) => {\n log.warn('generate-home-suggestions: LLM call failed', { err });\n return Effect.succeed<string[]>([]);\n }),\n Effect.provide(AiService.languageModel(MODEL)),\n );\n\nexport default handler;\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DXN } from "@dxos/keys";
|
|
2
2
|
import { Model, Provider } from "@dxos/ai";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/chat-model/presets.ts
|
|
4
4
|
/** Presets for a provider: every model the provider serves (the catalog filtered by provider). */
|
|
5
5
|
var presetsForProvider = (provider) => Model.forProvider(provider).map((model) => ({
|
|
6
6
|
id: model.id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-presets.mjs","names":[],"sources":["../../src/
|
|
1
|
+
{"version":3,"file":"chunk-presets.mjs","names":[],"sources":["../../src/chat-model/presets.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Model, Provider } from '@dxos/ai';\nimport { DXN } from '@dxos/keys';\n\n/**\n * A chat preset: a model offered for a provider, with a display label. Derived from the {@link Model}\n * catalog — see {@link presetsForProvider}.\n */\nexport type AiServicePreset = {\n id: string;\n provider: DXN.DXN;\n model: DXN.DXN;\n /** Provider-specific back-end name (e.g. an Ollama pull tag); used to match installed models. */\n backend: string;\n label: string;\n};\n\n/** Presets for a provider: every model the provider serves (the catalog filtered by provider). */\nexport const presetsForProvider = (provider: DXN.DXN): AiServicePreset[] =>\n Model.forProvider(provider).map((model) => ({\n id: model.id,\n provider,\n model: model.id,\n backend: model.backend,\n label: model.label,\n }));\n\n/**\n * The model a chat runs on when settings name none — chosen explicitly rather than inherited from the\n * catalog's order, whose first edge entry is the most expensive model.\n */\nexport const DEFAULT_MODEL: DXN.DXN = Model.claudeSonnet5.id;\n\n/**\n * The preset a chat settles on: the configured default when the list still offers it, else\n * {@link DEFAULT_MODEL}, else the first available one.\n */\nexport const pickPreset = (presets: readonly AiServicePreset[], defaultModel?: string): AiServicePreset | undefined =>\n (defaultModel ? presets.find((preset) => preset.model === defaultModel) : undefined) ??\n presets.find((preset) => preset.model === DEFAULT_MODEL) ??\n presets[0];\n\n/**\n * The preset a chat with no model of its own falls back to, derived from settings alone — what\n * `usePresets` shows for such a chat.\n *\n * Exported for callers outside React that start a turn on a chat's behalf (delegation, a routine):\n * the agent process is bound to the model on the chat, so a caller stamping anything but this onto\n * an unselected chat would change what its own UI then shows. Availability filtering (installed\n * sidecar models, a reachable LM Studio) is the UI's; this answers from the catalog, which is exact\n * for `edge` and the configured default everywhere else.\n */\nexport const defaultPreset = (\n settings: { modelProvider?: string; modelDefaults?: Record<string, string | undefined> },\n options?: { hasBuiltIn?: boolean },\n): AiServicePreset | undefined => {\n const provider = resolveProvider(settings.modelProvider, options?.hasBuiltIn ?? false);\n return pickPreset(presetsForProvider(provider), settings.modelDefaults?.[defaultsKeyForProvider(provider)]);\n};\n\n/**\n * The provider to resolve `model` through, given the one settings currently select. Model ids are\n * provider-scoped, so a chat that kept a selection across a provider change (it picked Claude, then\n * the user went offline) would otherwise be handed a provider that does not serve it and fail to\n * resolve. The active provider wins whenever it serves the model — several providers serve the same\n * local model ids, and the catalog's first entry is not necessarily the live one.\n */\nexport const providerForModel = (model: DXN.DXN, active: DXN.DXN | undefined): DXN.DXN | undefined =>\n active && Model.get(active, model) ? active : (Model.byId(model)[0]?.provider ?? active);\n\n/**\n * Reconcile a stored provider DXN with the runtime: map the bundled sidecar (`built-in`) and an\n * external server (`ollama`) onto whichever is actually available — they are environment-exclusive\n * (the sidecar exists only on desktop). Defaults to `edge` when unset or unparseable.\n */\nexport const resolveProvider = (provider: string | undefined, hasBuiltIn: boolean): DXN.DXN => {\n const resolved = (provider ? DXN.tryMake(provider) : undefined) ?? Provider.edge.id;\n if (resolved === Provider.ollama.id && hasBuiltIn) {\n return Provider.builtIn.id;\n }\n if (resolved === Provider.builtIn.id && !hasBuiltIn) {\n return Provider.ollama.id;\n }\n return resolved;\n};\n\n/** The {@link Settings.modelDefaults} key for a provider (`built-in` shares the `ollama` key). */\nexport const defaultsKeyForProvider = (provider: DXN.DXN): 'edge' | 'ollama' | 'lmstudio' =>\n provider === Provider.edge.id ? 'edge' : provider === Provider.lmStudio.id ? 'lmstudio' : 'ollama';\n"],"mappings":";;;;AAqBA,IAAa,sBAAsB,aACjC,MAAM,YAAY,QAAQ,CAAC,CAAC,KAAK,WAAW;CAC1C,IAAI,MAAM;CACV;CACA,OAAO,MAAM;CACb,SAAS,MAAM;CACf,OAAO,MAAM;AACf,EAAE;;;;;AAMJ,IAAa,gBAAyB,MAAM,cAAc;;;;;AAM1D,IAAa,cAAc,SAAqC,kBAC7D,eAAe,QAAQ,MAAM,WAAW,OAAO,UAAU,YAAY,IAAI,KAAA,MAC1E,QAAQ,MAAM,WAAW,OAAO,UAAU,aAAa,KACvD,QAAQ;;;;;;;;;;;AAYV,IAAa,iBACX,UACA,YACgC;CAChC,MAAM,WAAW,gBAAgB,SAAS,eAAe,SAAS,cAAc,KAAK;CACrF,OAAO,WAAW,mBAAmB,QAAQ,GAAG,SAAS,gBAAgB,uBAAuB,QAAQ,EAAE;AAC5G;;;;;;;;AASA,IAAa,oBAAoB,OAAgB,WAC/C,UAAU,MAAM,IAAI,QAAQ,KAAK,IAAI,SAAU,MAAM,KAAK,KAAK,CAAC,CAAC,EAAE,EAAE,YAAY;;;;;;AAOnF,IAAa,mBAAmB,UAA8B,eAAiC;CAC7F,MAAM,YAAY,WAAW,IAAI,QAAQ,QAAQ,IAAI,KAAA,MAAc,SAAS,KAAK;CACjF,IAAI,aAAa,SAAS,OAAO,MAAM,YACrC,OAAO,SAAS,QAAQ;CAE1B,IAAI,aAAa,SAAS,QAAQ,MAAM,CAAC,YACvC,OAAO,SAAS,OAAO;CAEzB,OAAO;AACT;;AAGA,IAAa,0BAA0B,aACrC,aAAa,SAAS,KAAK,KAAK,SAAS,aAAa,SAAS,SAAS,KAAK,aAAa"}
|
|
@@ -6,10 +6,10 @@ import * as Chat from "@dxos/assistant/Chat";
|
|
|
6
6
|
import { Obj } from "@dxos/echo";
|
|
7
7
|
import { log } from "@dxos/log";
|
|
8
8
|
import { Task } from "@dxos/types";
|
|
9
|
-
import * as TaskOperation from "@dxos/plugin-tasks/TaskOperation";
|
|
10
9
|
import * as Cause from "effect/Cause";
|
|
11
10
|
import * as Effect from "effect/Effect";
|
|
12
11
|
import * as Stream from "effect/Stream";
|
|
12
|
+
import * as TaskOperation from "@dxos/plugin-tasks/TaskOperation";
|
|
13
13
|
//#region src/capabilities/question-resumer.ts
|
|
14
14
|
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-assistant/src/capabilities/question-resumer.ts";
|
|
15
15
|
/** Narrows an invocation to the task pane's answer, whose input and output its definition types. */
|
|
@@ -11,10 +11,10 @@ import { EID } from "@dxos/keys";
|
|
|
11
11
|
import { jsx } from "react/jsx-runtime";
|
|
12
12
|
import { log } from "@dxos/log";
|
|
13
13
|
import * as Effect from "effect/Effect";
|
|
14
|
+
import * as Agent from "@dxos/assistant/Agent";
|
|
14
15
|
import * as Surface from "@dxos/app-framework/Surface";
|
|
15
16
|
import * as AppSurface from "@dxos/app-toolkit/AppSurface";
|
|
16
17
|
import { Sequence } from "@dxos/conductor";
|
|
17
|
-
import * as Agent from "@dxos/assistant/Agent";
|
|
18
18
|
import * as Instructions from "@dxos/compute/Instructions";
|
|
19
19
|
import { getSpace } from "@dxos/client/echo";
|
|
20
20
|
import * as Position from "@dxos/util/Position";
|
|
@@ -8,6 +8,7 @@ import { Obj } from "@dxos/echo";
|
|
|
8
8
|
import { ContentBlock } from "@dxos/types";
|
|
9
9
|
import { BaseError } from "@dxos/errors";
|
|
10
10
|
import * as Effect from "effect/Effect";
|
|
11
|
+
import * as Agent from "@dxos/assistant/Agent";
|
|
11
12
|
import * as AgentService from "@dxos/compute/AgentService";
|
|
12
13
|
import * as Operation from "@dxos/compute/Operation";
|
|
13
14
|
import * as Plugin from "@dxos/app-framework/Plugin";
|
|
@@ -49,7 +50,7 @@ var handler = AssistantOperation.RunPromptInChat.pipe(Operation.withHandler(Effe
|
|
|
49
50
|
const model = chat.session?.model ?? preset?.model;
|
|
50
51
|
yield* (yield* AgentService.getSession(chat, {
|
|
51
52
|
provider: model ? providerForModel(model, preset?.provider) : preset?.provider,
|
|
52
|
-
location: chat
|
|
53
|
+
location: Agent.chatLocation(chat)
|
|
53
54
|
})).submitPrompt(disposition === void 0 ? prompt : [ContentBlock.Text.make({
|
|
54
55
|
text: prompt,
|
|
55
56
|
disposition
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chunk-run-prompt-in-chat.mjs","names":[],"sources":["../../src/errors.ts","../../src/operations/run-prompt-in-chat.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError } from '@dxos/errors';\n\n/** `runPromptInChat` was given neither a chat nor an object whose companion chat should run it. */\nexport class ChatNotSpecifiedError extends BaseError.extend('ChatNotSpecifiedError', 'Pass `chat` or `companionTo`.') {}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Plugin from '@dxos/app-framework/Plugin';\nimport * as Chat from '@dxos/assistant/Chat';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Obj } from '@dxos/echo';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\nimport { ContentBlock } from '@dxos/types';\n\nimport { AssistantCapabilities, AssistantEvents, AssistantOperation } from '#types';\n\nimport {
|
|
1
|
+
{"version":3,"file":"chunk-run-prompt-in-chat.mjs","names":[],"sources":["../../src/errors.ts","../../src/operations/run-prompt-in-chat.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { BaseError } from '@dxos/errors';\n\n/** `runPromptInChat` was given neither a chat nor an object whose companion chat should run it. */\nexport class ChatNotSpecifiedError extends BaseError.extend('ChatNotSpecifiedError', 'Pass `chat` or `companionTo`.') {}\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Plugin from '@dxos/app-framework/Plugin';\nimport * as Agent from '@dxos/assistant/Agent';\nimport * as Chat from '@dxos/assistant/Chat';\nimport * as AgentService from '@dxos/compute/AgentService';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Obj } from '@dxos/echo';\nimport * as SpaceOperation from '@dxos/plugin-space/SpaceOperation';\nimport { ContentBlock } from '@dxos/types';\n\nimport { AssistantCapabilities, AssistantEvents, AssistantOperation } from '#types';\n\nimport { defaultPreset, providerForModel } from '../chat-model/index.ts';\nimport { ChatNotSpecifiedError } from '../errors.ts';\n\nconst handler: Operation.WithHandler<typeof AssistantOperation.RunPromptInChat> =\n AssistantOperation.RunPromptInChat.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* ({ chat: chatProp, companionTo, prompt, disposition }) {\n // Activation first: the state and session providers this reads come from lazy modules that\n // otherwise activate only once the assistant UI has been opened, so a caller arriving through\n // an operation alone (an agent) would find them missing.\n const pluginManager = yield* Effect.serviceOption(Plugin.Service);\n yield* Option.match(pluginManager, {\n onNone: () => Effect.void,\n onSome: (manager) => manager.activate(AssistantEvents.Start),\n });\n const companion =\n chatProp === undefined && companionTo !== undefined\n ? yield* Operation.invoke(AssistantOperation.EnsureCompanionChat, { companionTo })\n : undefined;\n const chat = chatProp ?? companion?.chat;\n if (chat === undefined) {\n return yield* Effect.fail(new ChatNotSpecifiedError());\n }\n // As the companion's own submit does: a transient chat is persisted under its subject before\n // the first request, so the agent process can resolve a durable conversation feed and space.\n const db = companionTo !== undefined ? Obj.getDatabase(companionTo) : undefined;\n if (companionTo !== undefined && db && !Obj.getDatabase(chat)) {\n Chat.linkCompanion({ chat, subject: companionTo });\n yield* Operation.invoke(SpaceOperation.AddObject, { object: chat }, { spaceId: db.spaceId });\n yield* Operation.invoke(AssistantOperation.SetCurrentChat, { companionTo, chat });\n yield* Effect.promise(() => db.flush());\n }\n const preset = yield* chatPreset;\n // As the chat's own UI does before its first request: the process reads the model off the\n // chat, so a chat without one is stamped with the model its picker would show.\n if (!chat.session?.model && preset) {\n Obj.update(chat, (chat) => {\n chat.session = { ...chat.session, model: preset.model };\n });\n }\n // The model is the chat's, so the provider has to be the one that serves THAT model rather\n // than whichever the settings now name — a chat outlives a provider change.\n const model = chat.session?.model ?? preset?.model;\n const session = yield* AgentService.getSession(chat, {\n provider: model ? providerForModel(model, preset?.provider) : preset?.provider,\n location: Agent.chatLocation(chat),\n });\n // A plain string is submitted as-is so the default path keeps its existing shape; a stated\n // disposition needs the block form, which is the only place it can be carried.\n yield* session.submitPrompt(\n disposition === undefined ? prompt : [ContentBlock.Text.make({ text: prompt, disposition })],\n );\n }),\n ),\n );\n\n/**\n * The preset the chat's UI would run with. Absent settings (a host with no assistant UI) leaves the\n * model unset, which is the agent process's own default.\n */\nconst chatPreset = Effect.gen(function* () {\n const settings = yield* Capabilities.getAtomValueOption(AssistantCapabilities.Settings);\n // The bundled sidecar's presence is what makes `built-in` rather than `ollama` the live provider.\n const ollama = yield* Capability.getOption(AssistantCapabilities.OllamaManager);\n return Option.match(settings, {\n onNone: () => undefined,\n onSome: (settings) => defaultPreset(settings, { hasBuiltIn: Option.isSome(ollama) }),\n });\n});\n\nexport default handler;\n"],"mappings":";;;;;;;;;;;;;;;;;AAOA,IAAa,wBAAb,cAA2C,UAAU,OAAO,yBAAyB,+BAA+B,CAAC,CAAC,CAAC;;;ACgBvH,IAAM,UACJ,mBAAmB,gBAAgB,KACjC,UAAU,YACR,OAAO,WAAW,WAAW,EAAE,MAAM,UAAU,aAAa,QAAQ,eAAe;CAIjF,MAAM,gBAAgB,OAAO,OAAO,cAAc,OAAO,OAAO;CAChE,OAAO,OAAO,MAAM,eAAe;EACjC,cAAc,OAAO;EACrB,SAAS,YAAY,QAAQ,SAAS,gBAAgB,KAAK;CAC7D,CAAC;CACD,MAAM,YACJ,aAAa,KAAA,KAAa,gBAAgB,KAAA,IACtC,OAAO,UAAU,OAAO,mBAAmB,qBAAqB,EAAE,YAAY,CAAC,IAC/E,KAAA;CACN,MAAM,OAAO,YAAY,WAAW;CACpC,IAAI,SAAS,KAAA,GACX,OAAO,OAAO,OAAO,KAAK,IAAI,sBAAsB,CAAC;CAIvD,MAAM,KAAK,gBAAgB,KAAA,IAAY,IAAI,YAAY,WAAW,IAAI,KAAA;CACtE,IAAI,gBAAgB,KAAA,KAAa,MAAM,CAAC,IAAI,YAAY,IAAI,GAAG;EAC7D,KAAK,cAAc;GAAE;GAAM,SAAS;EAAY,CAAC;EACjD,OAAO,UAAU,OAAO,eAAe,WAAW,EAAE,QAAQ,KAAK,GAAG,EAAE,SAAS,GAAG,QAAQ,CAAC;EAC3F,OAAO,UAAU,OAAO,mBAAmB,gBAAgB;GAAE;GAAa;EAAK,CAAC;EAChF,OAAO,OAAO,cAAc,GAAG,MAAM,CAAC;CACxC;CACA,MAAM,SAAS,OAAO;CAGtB,IAAI,CAAC,KAAK,SAAS,SAAS,QAC1B,IAAI,OAAO,OAAO,SAAS;EACzB,KAAK,UAAU;GAAE,GAAG,KAAK;GAAS,OAAO,OAAO;EAAM;CACxD,CAAC;CAIH,MAAM,QAAQ,KAAK,SAAS,SAAS,QAAQ;CAO7C,QAAO,OANgB,aAAa,WAAW,MAAM;EACnD,UAAU,QAAQ,iBAAiB,OAAO,QAAQ,QAAQ,IAAI,QAAQ;EACtE,UAAU,MAAM,aAAa,IAAI;CACnC,CAAC,EAAA,CAGc,aACb,gBAAgB,KAAA,IAAY,SAAS,CAAC,aAAa,KAAK,KAAK;EAAE,MAAM;EAAQ;CAAY,CAAC,CAAC,CAC7F;AACF,CAAC,CACH,CACF;;;;;AAMF,IAAM,aAAa,OAAO,IAAI,aAAa;CACzC,MAAM,WAAW,OAAO,aAAa,mBAAmB,sBAAsB,QAAQ;CAEtF,MAAM,SAAS,OAAO,WAAW,UAAU,sBAAsB,aAAa;CAC9E,OAAO,OAAO,MAAM,UAAU;EAC5B,cAAc,KAAA;EACd,SAAS,aAAa,cAAc,UAAU,EAAE,YAAY,OAAO,OAAO,MAAM,EAAE,CAAC;CACrF,CAAC;AACH,CAAC"}
|
|
@@ -8,8 +8,8 @@ import { AiContext } from "@dxos/assistant";
|
|
|
8
8
|
import { log } from "@dxos/log";
|
|
9
9
|
import * as Effect from "effect/Effect";
|
|
10
10
|
import * as Operation from "@dxos/compute/Operation";
|
|
11
|
-
import * as ClientCapabilities from "@dxos/plugin-client/ClientCapabilities";
|
|
12
11
|
import * as Skill from "@dxos/compute/Skill";
|
|
12
|
+
import * as ClientCapabilities from "@dxos/plugin-client/ClientCapabilities";
|
|
13
13
|
import * as Template from "@dxos/compute/Template";
|
|
14
14
|
import { Text } from "@dxos/schema";
|
|
15
15
|
import { invariant } from "@dxos/invariant";
|
|
@@ -2,11 +2,11 @@ import * as Chat from "@dxos/assistant/Chat";
|
|
|
2
2
|
import { Feed } from "@dxos/echo";
|
|
3
3
|
import { AiContext, Alarm, SessionLink } from "@dxos/assistant";
|
|
4
4
|
import { HasSubject, Message } from "@dxos/types";
|
|
5
|
-
import * as
|
|
5
|
+
import * as Agent from "@dxos/assistant/Agent";
|
|
6
6
|
import * as Skill from "@dxos/compute/Skill";
|
|
7
|
+
import * as McpServer from "@dxos/compute/McpServer";
|
|
7
8
|
import { Text } from "@dxos/schema";
|
|
8
9
|
import { Sequence } from "@dxos/conductor";
|
|
9
|
-
import * as Agent from "@dxos/assistant/Agent";
|
|
10
10
|
import * as Instructions from "@dxos/compute/Instructions";
|
|
11
11
|
import * as Memory from "@dxos/assistant-toolkit/Memory";
|
|
12
12
|
//#region src/capabilities/schema-defs.ts
|