@powerhousedao/reactor-browser 6.2.2-dev.84 → 6.2.2-dev.85
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/dist/{document-model-modules-DMIR-uSD.js → document-model-modules-DfQBNGc-.js} +11 -3
- package/dist/{document-model-modules-DMIR-uSD.js.map → document-model-modules-DfQBNGc-.js.map} +1 -1
- package/dist/{document-operations-Bo1dPDgq.js → document-operations-CBExT76y.js} +2 -2
- package/dist/{document-operations-Bo1dPDgq.js.map → document-operations-CBExT76y.js.map} +1 -1
- package/dist/index.d.ts +26 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -10
- package/dist/index.js.map +1 -1
- package/dist/src/ai/index.d.ts +3 -1
- package/dist/src/ai/index.d.ts.map +1 -1
- package/dist/src/ai/index.js +3 -232
- package/dist/src/ai/index.js.map +1 -1
- package/dist/src/graphql-client/entry.js +2 -2
- package/dist/switchboard-D4-_jwk-.js +271 -0
- package/dist/switchboard-D4-_jwk-.js.map +1 -0
- package/package.json +9 -9
- package/dist/selected-document-xFBNPc9Q.js +0 -42
- package/dist/selected-document-xFBNPc9Q.js.map +0 -1
package/dist/src/ai/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["formatArgs"],"sources":["../../../src/ai/types.ts","../../../src/ai/agent.ts","../../../src/ai/components/approval-card.tsx","../../../src/ai/components/markdown.tsx","../../../src/ai/components/chat-window.tsx","../../../src/ai/settings-store.ts","../../../src/ai/switchboard.ts","../../../src/ai/context.ts","../../../src/ai/use-reactor-chat.ts","../../../src/ai/components/reactor-chat-fab.tsx"],"sourcesContent":["import type {\n PhAiToolAnnotations,\n PhAiToolDescriptor,\n} from \"@powerhousedao/shared/document-model\";\n\n/**\n * Browser-side AI chat over the reactor.\n *\n * The chat agent is tool-agnostic: consumers (e.g. Connect) pass a lazy\n * provider of {@link AiToolDescriptor}s. The descriptors mirror the\n * provider-agnostic tool core in `@powerhousedao/reactor-mcp/tools`, so the\n * same tool definitions drive both the MCP server and the in-browser chat.\n */\n\n/** User-configured LLM connection settings, persisted in localStorage. */\nexport interface AiSettings {\n /** Master on/off for the in-browser assistant. Off by default. */\n enabled: boolean;\n /** OpenAI-compatible base URL, e.g. `https://api.openai.com/v1` */\n baseUrl: string;\n /** API key. Sent only to the configured endpoint, never to Powerhouse. */\n apiKey: string;\n /** Model id the endpoint serves, e.g. `gpt-4o-mini`. */\n model: string;\n /**\n * When false (default), write tools render an approval card and pause the\n * agent loop until the user approves or rejects the action.\n */\n autoApproveWrites: boolean;\n}\n\n/** MCP-compatible annotation hints; structural, no MCP SDK dependency. */\nexport type AiToolAnnotations = PhAiToolAnnotations;\n\n/**\n * Provider-agnostic tool descriptor accepted by the chat agent.\n *\n * Structurally compatible with the MCP tool records produced by\n * `createReactorMcpProvider`: the callback parameter is `never` so any\n * per-tool-args function type is assignable, and the result is `unknown`\n * because the envelope shape (MCP `CallToolResult`) is unwrapped at the\n * adapter boundary.\n */\nexport type AiToolDescriptor = PhAiToolDescriptor;\n\n/** Tool descriptors the app resolves when the user sends a message. */\nexport type AiToolsProvider = () => Promise<AiToolDescriptor[]>;\n\n/** Tool names whose execution mutates the reactor and requires approval. */\nexport const WRITE_TOOLS: ReadonlySet<string> = new Set([\n \"createDocument\",\n \"addActions\",\n \"deleteDocument\",\n \"addDrive\",\n \"deleteDrive\",\n \"addRemoteDrive\",\n]);\n\n/**\n * Whether executing the tool mutates state and requires user approval:\n * the built-in write tools, or any tool flagged destructive in its\n * annotations (covers package-provided tools outside the built-in set).\n */\nexport function isWriteTool(\n name: string,\n annotations?: AiToolAnnotations,\n): boolean {\n return WRITE_TOOLS.has(name) || annotations?.destructiveHint === true;\n}\n\nexport type ToolCallState =\n | \"awaiting-approval\"\n | \"executing\"\n | \"done\"\n | \"error\"\n | \"rejected\";\n\n/** One rendered unit inside a chat message. */\nexport type ChatPart =\n | { type: \"text\"; text: string }\n | {\n type: \"tool\";\n toolCallId: string;\n name: string;\n args: unknown;\n state: ToolCallState;\n result?: unknown;\n error?: string;\n };\n\nexport interface ChatMessage {\n id: string;\n role: \"user\" | \"assistant\";\n parts: ChatPart[];\n}\n\n/** A write tool call the user must approve or reject. */\nexport interface PendingApproval {\n toolCallId: string;\n name: string;\n args: unknown;\n}\n\n/** Context snapshot injected into the agent system prompt. */\nexport interface ChatContext {\n driveId?: string;\n driveName?: string;\n nodeId?: string;\n nodeName?: string;\n nodeKind?: \"file\" | \"folder\";\n documentType?: string;\n documentName?: string;\n documentId?: string;\n switchboardUrl?: string;\n switchboardGraphqlUrl?: string;\n}\n\n/** Events the agent emits while running a turn. */\nexport type AgentEvent =\n | { type: \"text-delta\"; delta: string }\n | {\n type: \"tool-start\";\n toolCallId: string;\n name: string;\n args: unknown;\n }\n | {\n type: \"approval-request\";\n toolCallId: string;\n name: string;\n args: unknown;\n }\n | { type: \"approval-resolved\"; toolCallId: string; approved: boolean }\n | {\n type: \"tool-result\";\n toolCallId: string;\n name: string;\n state: \"done\" | \"error\" | \"rejected\";\n result?: unknown;\n error?: string;\n }\n /**\n * Token accounting for the turn: the input tokens of the model's last\n * step (i.e. the size of the context it was reasoning over).\n */\n | { type: \"usage\"; inputTokens: number }\n | { type: \"finish\" }\n | { type: \"error\"; error: string };\n","import {\n type LanguageModel,\n type ModelMessage,\n type Tool,\n isStepCount,\n streamText,\n tool,\n} from \"ai\";\nimport { toResponseMessages } from \"ai/internal\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport { z } from \"zod\";\nimport { isWriteTool } from \"./types.js\";\nimport type {\n AgentEvent,\n AiSettings,\n AiToolDescriptor,\n ChatContext,\n} from \"./types.js\";\n\n/** Hard cap on model round-trips per user message (each tool step counts). */\nexport const MAX_AGENT_STEPS = 10;\n\n/**\n * Hard cap on the characters of one tool result fed back to the model.\n *\n * Large results (whole document states, catalog listings, schema\n * summaries) accumulate across steps because every step resends the whole\n * conversation; without a budget a few broad queries exhaust the model's\n * context and stall the turn. Over-budget results are truncated with a\n * marker so the model re-queries more narrowly.\n */\nexport const MAX_TOOL_RESULT_CHARS = 12_000;\n\nconst TRUNCATION_NOTE =\n \"\\n...[truncated: this tool result exceeded the context budget; re-query with a more specific filter or fewer items]\";\n\nfunction boundedToolResult(value: unknown): unknown {\n const text = resultText(value);\n if (text.length <= MAX_TOOL_RESULT_CHARS) return value;\n return text.slice(0, MAX_TOOL_RESULT_CHARS) + TRUNCATION_NOTE;\n}\n\nfunction resultText(value: unknown): string {\n if (typeof value === \"string\") return value;\n const json = JSON.stringify(value);\n // JSON.stringify yields undefined at runtime for undefined inputs\n // even though its type signature says otherwise.\n return typeof json === \"string\" ? json : \"undefined\";\n}\n\n/**\n * Number of the most recent turns whose tool results are kept in full in\n * the committed history. Tool results are re-derivable (the store is the\n * source of truth), so results older than that window are replaced with\n * one-line stubs: this is what keeps a long conversation from exhausting\n * the model's context.\n */\nexport const FULL_RESULT_TURNS = 2;\n\n/** Tool results smaller than this are cheap to keep and are never stubbed. */\nexport const STUB_MIN_CHARS = 500;\n\nfunction toolResultChars(output: unknown): number {\n const part = output as { type?: string; value?: unknown };\n if (part.type === \"text\" && typeof part.value === \"string\") {\n return part.value.length;\n }\n const target = part.value !== undefined ? part.value : output;\n const json = JSON.stringify(target);\n return typeof json === \"string\" ? json.length : 0;\n}\n\nfunction argDigest(input: unknown): string {\n if (input === undefined) return \"\";\n let text: string;\n try {\n text = JSON.stringify(input);\n } catch {\n // Circular structures (impossible for parsed tool args) get an\n // empty digest rather than \"[object Object]\".\n text = \"\";\n }\n if (text.length <= 80) return text;\n return `${text.slice(0, 77)}…`;\n}\n\n/**\n * Replaces tool results older than the last {@link FULL_RESULT_TURNS}\n * turns with one-line stubs naming the tool and its arguments. User and\n * assistant messages are never touched, and tool calls keep their input,\n * so the model can re-query any stubbed result with the same arguments.\n * Idempotent: stubs are below {@link STUB_MIN_CHARS} and pass through.\n */\nexport function stubStaleToolResults(history: ModelMessage[]): ModelMessage[] {\n // Window start: position of the Nth-from-last user message.\n let users = 0;\n let windowStart = 0;\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].role === \"user\") {\n users += 1;\n if (users === FULL_RESULT_TURNS) {\n windowStart = i;\n break;\n }\n }\n }\n if (users < FULL_RESULT_TURNS) return history;\n\n // Argument digests for the stubs, looked up by tool call id.\n const callInputs = new Map<string, unknown>();\n for (const m of history) {\n if (m.role === \"assistant\" && Array.isArray(m.content)) {\n for (const part of m.content) {\n if (part.type === \"tool-call\") {\n callInputs.set(part.toolCallId, part.input);\n }\n }\n }\n }\n\n return history.map((m, i) => {\n if (i >= windowStart || m.role !== \"tool\") return m;\n const content = m.content.map((part) => {\n if (part.type !== \"tool-result\") return part;\n const chars = toolResultChars(part.output);\n if (chars < STUB_MIN_CHARS) return part;\n const stub: { type: \"text\"; value: string } = {\n type: \"text\",\n value: `[result omitted: ${part.toolName} ${argDigest(\n callInputs.get(part.toolCallId),\n )} — ${chars} chars; re-query the tool to refetch]`,\n };\n return { ...part, output: stub };\n });\n const changed = content.some((part, index) => part !== m.content[index]);\n if (!changed) return m;\n return { ...m, content };\n });\n}\n\n/**\n * Creates the chat language model for a user-supplied OpenAI-compatible\n * endpoint. Requests go directly from the browser to the endpoint; the API\n * key is never sent to any Powerhouse server.\n */\n\n/**\n * Resolves the configured base URL. Relative paths (e.g. `/v1`) resolve\n * against the page origin, so single-origin deployments can serve the\n * endpoint behind the same reverse proxy without CORS.\n */\nfunction resolveBaseUrl(raw: string): string {\n const base = raw.trim().replace(/\\/+$/, \"\");\n if (/^https?:\\/\\//i.test(base)) return base;\n if (typeof window === \"undefined\") return base;\n return `${window.location.origin}${base.startsWith(\"/\") ? \"\" : \"/\"}${base}`;\n}\n\nexport function createReactorChatModel(settings: AiSettings): LanguageModel {\n const provider = createOpenAICompatible({\n name: \"reactor-chat\",\n baseURL: resolveBaseUrl(settings.baseUrl),\n apiKey: settings.apiKey,\n });\n return provider.chatModel(settings.model.trim());\n}\n\n/**\n * Unwraps the MCP `CallToolResult` envelope produced by the reactor tool\n * core into a plain value for the AI SDK. Throws for error results so the\n * SDK surfaces them as tool errors in the next model step.\n */\nexport function unwrapToolResult(raw: unknown): unknown {\n if (raw && typeof raw === \"object\") {\n const envelope = raw as {\n isError?: boolean;\n content?: Array<{ type?: string; text?: string }>;\n structuredContent?: unknown;\n };\n if (\n typeof envelope.isError === \"boolean\" ||\n Array.isArray(envelope.content)\n ) {\n if (envelope.isError) {\n const text =\n envelope.content?.find((c) => c.type === \"text\")?.text ??\n \"Unknown tool error\";\n throw new Error(text.replace(/^Error:\\s*/, \"\"));\n }\n if (envelope.structuredContent !== undefined) {\n return envelope.structuredContent;\n }\n const textPart = envelope.content?.find((c) => c.type === \"text\");\n if (textPart?.text !== undefined) {\n try {\n return JSON.parse(textPart.text);\n } catch {\n return textPart.text;\n }\n }\n return null;\n }\n }\n return raw;\n}\n\n/** Builds the system prompt, grounding the agent in the current selection. */\nexport function buildSystemPrompt(context: ChatContext): string {\n const lines: string[] = [\n \"You are an assistant embedded in the Powerhouse Connect drive explorer.\",\n \"You operate on a local-first document store (the reactor) exclusively through the provided tools.\",\n \"Documents are instances of typed document models; drives are collections that group documents and folders.\",\n \"Prefer read-only tools to discover state (document models, drives, documents, relationships) before making changes.\",\n \"Never invent document ids, drive ids, folder ids or document model types — discover them with the read tools first.\",\n \"Never accept secret values (passwords, tokens, API keys) in chat. When a connection or configuration requires a secret, tell the user to enter it in the relevant editor (e.g. the connection editor) and point them to the document. Never ask the user to paste a secret into the chat.\",\n \"When the user refers to 'this', 'here' or 'it' without naming a target, they mean the current selection below; prefer it for create/modify targets.\",\n \"Tool results may be truncated when they are large: if you see a truncation marker, narrow the query (more specific filter, fewer items) instead of retrying the same call.\",\n \"Older tool results in this conversation may be replaced by a short '[result omitted: ...]' stub as the context grows; that is normal housekeeping, not an error. If you still need the data, call the same tool again with the same arguments.\",\n ];\n const selection: string[] = [];\n if (context.driveName) {\n selection.push(\n `The current drive is \"${context.driveName}\"` +\n (context.driveId ? ` (id: ${context.driveId})` : \"\") +\n \".\",\n );\n }\n if (context.nodeKind === \"folder\" && context.nodeName) {\n selection.push(`The current folder is \"${context.nodeName}\".`);\n }\n if (context.documentType) {\n selection.push(\n `The current document is \"${context.documentName ?? \"unnamed\"}\" of type \"${context.documentType}\"` +\n (context.documentId ? ` (id: ${context.documentId})` : \"\") +\n \".\",\n );\n }\n if (context.switchboardUrl) {\n selection.push(\n `The switchboard for this drive is at ${context.switchboardUrl}; its GraphQL endpoint is ${context.switchboardGraphqlUrl ?? \"\"}. Use the getSwitchboardSchema tool to list the queries and mutations it exposes.`,\n );\n } else if (context.driveId) {\n selection.push(\n \"This drive is not synced to a switchboard, so no switchboard endpoints are available for it.\",\n );\n }\n if (selection.length > 0) {\n lines.push(\"Current selection:\", ...selection.map((s) => `- ${s}`));\n } else {\n lines.push(\n \"Nothing is currently selected. When the target of an action is ambiguous, ask the user which drive, folder or document it should apply to.\",\n );\n }\n return lines.join(\"\\n\");\n}\n\nexport interface ReactorChatAgentOptions {\n settings: AiSettings;\n tools: AiToolDescriptor[];\n context: ChatContext;\n onEvent: (event: AgentEvent) => void;\n signal?: AbortSignal;\n /** Test seam: override the model (e.g. a mock language model). */\n model?: LanguageModel;\n /** Prior conversation to continue (see {@link ReactorChatAgent.getHistory}). */\n history?: ModelMessage[];\n}\n\n/**\n * Runs one tool-loop conversation turn against an OpenAI-compatible model.\n *\n * The agent keeps the model message history across turns. Write tools are\n * gated by the AI SDK's tool approval: when auto-approval is off, the\n * pending user decision is bridged through {@link approve}/{@link reject}.\n */\nexport class ReactorChatAgent {\n private history: ModelMessage[];\n private approvals = new Map<string, (approved: boolean) => void>();\n\n constructor(private readonly options: ReactorChatAgentOptions) {\n this.history = options.history ?? [];\n }\n\n /** The model history after the last completed turn, for the next agent. */\n getHistory(): ModelMessage[] {\n return this.history;\n }\n\n /** Clears the conversation history (a fresh chat). */\n reset(): void {\n this.history = [];\n this.cancelPendingApprovals();\n }\n\n /** Approves a pending write tool call, resuming the agent loop. */\n approve(toolCallId: string): void {\n this.resolveApproval(toolCallId, true);\n }\n\n /** Rejects a pending write tool call; the rejection is fed to the model. */\n reject(toolCallId: string): void {\n this.resolveApproval(toolCallId, false);\n }\n\n private resolveApproval(toolCallId: string, approved: boolean): void {\n const resolve = this.approvals.get(toolCallId);\n if (resolve) {\n this.approvals.delete(toolCallId);\n resolve(approved);\n }\n }\n\n private cancelPendingApprovals(): void {\n for (const resolve of this.approvals.values()) {\n resolve(false);\n }\n this.approvals.clear();\n }\n\n /** Runs one user turn: appends the message, streams the model, commits history. */\n async send(text: string): Promise<void> {\n const { onEvent, signal, settings } = this.options;\n const userMessage: ModelMessage = { role: \"user\", content: text };\n const messages: ModelMessage[] = [...this.history, userMessage];\n\n const tools: Record<string, Tool> = {};\n for (const descriptor of this.options.tools) {\n tools[descriptor.name] = tool({\n description: descriptor.description,\n inputSchema: z.object(descriptor.inputSchema),\n execute: async (args: unknown) =>\n boundedToolResult(\n unwrapToolResult(await descriptor.callback(args as never)),\n ),\n });\n }\n\n const model = this.options.model ?? createReactorChatModel(settings);\n\n // Messages produced by this turn: every completed step, converted\n // with the SDK's own toResponseMessages, so the committed history\n // is exactly the shape the model already saw mid-turn.\n const turnMessages: ModelMessage[] = [];\n let lastInputTokens: number | undefined;\n\n const result = streamText({\n model,\n system: buildSystemPrompt(this.options.context),\n messages,\n tools,\n stopWhen: isStepCount(MAX_AGENT_STEPS),\n abortSignal: signal,\n onStepEnd: async (step) => {\n turnMessages.push(\n ...(await toResponseMessages({ content: step.content, tools })),\n );\n if (typeof step.usage.inputTokens === \"number\") {\n lastInputTokens = step.usage.inputTokens;\n }\n },\n toolApproval: async ({ toolCall }) => {\n const descriptor = this.options.tools.find(\n (t) => t.name === toolCall.toolName,\n );\n if (\n settings.autoApproveWrites ||\n !isWriteTool(toolCall.toolName, descriptor?.annotations)\n ) {\n return \"not-applicable\";\n }\n const toolCallId = toolCall.toolCallId;\n onEvent({\n type: \"approval-request\",\n toolCallId,\n name: toolCall.toolName,\n args: toolCall.input,\n });\n const approved = await new Promise<boolean>((resolve) => {\n this.approvals.set(toolCallId, resolve);\n });\n onEvent({ type: \"approval-resolved\", toolCallId, approved });\n return approved\n ? \"approved\"\n : {\n type: \"denied\",\n reason:\n \"The user denied this action. Do not retry it; acknowledge the denial and ask how to proceed.\",\n };\n },\n });\n\n for await (const part of result.fullStream) {\n this.handleStreamPart(part, onEvent);\n }\n\n if (lastInputTokens !== undefined) {\n onEvent({ type: \"usage\", inputTokens: lastInputTokens });\n }\n\n // Commit the turn: the user message plus every completed step,\n // with orphaned tool calls dropped (step cap / abort), and stale\n // tool results stubbed so the context stays bounded across turns.\n this.history = stubStaleToolResults([\n ...this.history,\n userMessage,\n ...this.dropUnresolvedToolCalls(turnMessages),\n ]);\n onEvent({ type: \"finish\" });\n }\n\n /**\n * Removes tool calls that were never executed (step cap or abort):\n * OpenAI-compatible APIs reject a tool call without its tool result.\n */\n private dropUnresolvedToolCalls(messages: ModelMessage[]): ModelMessage[] {\n const resolved = new Set<string>();\n for (const m of messages) {\n if (m.role === \"tool\") {\n for (const part of m.content) {\n if (part.type === \"tool-result\") {\n resolved.add(part.toolCallId);\n }\n }\n }\n }\n const out = [...messages];\n if (out.length === 0) {\n return out;\n }\n const last = out[out.length - 1];\n if (last.role !== \"assistant\" || typeof last.content === \"string\") {\n return out;\n }\n const content = last.content.filter(\n (part) => part.type !== \"tool-call\" || resolved.has(part.toolCallId),\n );\n if (content.length === 0) {\n out.pop();\n } else {\n out[out.length - 1] = { ...last, content };\n }\n return out;\n }\n\n private handleStreamPart(\n part: unknown,\n onEvent: (e: AgentEvent) => void,\n ): void {\n const p = part as { type: string };\n switch (p.type) {\n case \"text-delta\": {\n const { text } = p as { type: \"text-delta\"; text: string };\n onEvent({ type: \"text-delta\", delta: text });\n break;\n }\n case \"tool-call\": {\n const { toolCallId, toolName, input } = p as {\n type: \"tool-call\";\n toolCallId: string;\n toolName: string;\n input: unknown;\n };\n onEvent({\n type: \"tool-start\",\n toolCallId,\n name: toolName,\n args: input,\n });\n break;\n }\n case \"tool-result\": {\n const { toolCallId, toolName, output } = p as {\n type: \"tool-result\";\n toolCallId: string;\n toolName: string;\n output: unknown;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"done\",\n result: output,\n });\n break;\n }\n case \"tool-error\": {\n const { toolCallId, toolName, error } = p as {\n type: \"tool-error\";\n toolCallId: string;\n toolName: string;\n error: unknown;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"error\",\n error: error instanceof Error ? error.message : String(error),\n });\n break;\n }\n case \"tool-approval-response\": {\n const { approved, toolCall } = p as unknown as {\n approved: boolean;\n toolCall: { toolCallId: string; toolName: string };\n };\n if (!approved) {\n onEvent({\n type: \"tool-result\",\n toolCallId: toolCall.toolCallId,\n name: toolCall.toolName,\n state: \"rejected\",\n });\n }\n break;\n }\n case \"tool-output-denied\": {\n const { toolCallId, toolName } = p as {\n type: \"tool-output-denied\";\n toolCallId: string;\n toolName: string;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"rejected\",\n });\n break;\n }\n case \"abort\":\n // The loop ends; the hook observes the aborted controller.\n break;\n case \"error\": {\n const { error } = p as { type: \"error\"; error: unknown };\n onEvent({\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n });\n break;\n }\n default:\n break;\n }\n }\n}\n","import { Check, X } from \"lucide-react\";\nimport type { PendingApproval } from \"../types.js\";\n\nfunction formatArgs(args: unknown): string {\n let json: string;\n try {\n json = JSON.stringify(args, null, 2);\n } catch {\n return String(args);\n }\n return json.length > 400 ? `${json.slice(0, 400)}\\n…` : json;\n}\n\n/**\n * In-chat approval card for a write tool call. Approving or rejecting\n * resolves the pending approval inside the agent loop.\n */\nexport function ApprovalCard({\n approval,\n onApprove,\n onReject,\n}: {\n approval: PendingApproval;\n onApprove: (toolCallId: string) => void;\n onReject: (toolCallId: string) => void;\n}) {\n return (\n <div className=\"rounded-lg border border-border bg-muted/40 p-3\">\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Approval required — {approval.name}\n </p>\n <pre className=\"mb-3 max-h-40 overflow-auto rounded bg-background p-2 text-xs text-foreground\">\n {formatArgs(approval.args)}\n </pre>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => onApprove(approval.toolCallId)}\n className=\"flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Check size={14} />\n Approve\n </button>\n <button\n type=\"button\"\n onClick={() => onReject(approval.toolCallId)}\n className=\"flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted\"\n >\n <X size={14} />\n Reject\n </button>\n </div>\n </div>\n );\n}\n","import ReactMarkdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\n/**\n * Markdown renderer for assistant messages.\n *\n * Uses react-markdown, which builds React elements from the markdown AST and\n * does not execute raw HTML — safe for model-generated content. remark-gfm\n * adds the table/strikethrough/task-list syntax the agent emits. Element\n * styling is scoped to the chat window's text-sm scale via Tailwind\n * utilities, so no extra CSS is needed.\n */\nexport function Markdown({ text }: { text: string }) {\n return (\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n components={{\n p: ({ children }) => (\n <p className=\"my-1.5 leading-relaxed first:mt-0 last:mb-0\">\n {children}\n </p>\n ),\n h1: ({ children }) => (\n <h1 className=\"mb-1 mt-3 font-semibold first:mt-0\">{children}</h1>\n ),\n h2: ({ children }) => (\n <h2 className=\"mb-1 mt-3 font-semibold first:mt-0\">{children}</h2>\n ),\n h3: ({ children }) => (\n <h3 className=\"mb-1 mt-2.5 font-semibold first:mt-0\">{children}</h3>\n ),\n ul: ({ children }) => (\n <ul className=\"my-1.5 list-disc space-y-0.5 pl-5\">{children}</ul>\n ),\n ol: ({ children }) => (\n <ol className=\"my-1.5 list-decimal space-y-0.5 pl-5\">{children}</ol>\n ),\n li: ({ children }) => <li className=\"leading-relaxed\">{children}</li>,\n blockquote: ({ children }) => (\n <blockquote className=\"my-1.5 border-l-2 border-border pl-2.5 text-muted-foreground\">\n {children}\n </blockquote>\n ),\n hr: () => <hr className=\"my-2 border-border\" />,\n a: ({ children, href }) => (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"text-primary underline\"\n >\n {children}\n </a>\n ),\n table: ({ children }) => (\n <div className=\"my-1.5 overflow-x-auto\">\n <table className=\"w-full border-collapse text-xs\">{children}</table>\n </div>\n ),\n thead: ({ children }) => <thead>{children}</thead>,\n th: ({ children }) => (\n <th className=\"border border-border bg-muted px-2 py-1 text-left font-semibold\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"border border-border px-2 py-1 align-top\">\n {children}\n </td>\n ),\n pre: ({ children }) => (\n <pre className=\"my-1.5 overflow-x-auto rounded-md bg-muted p-2.5 text-xs leading-relaxed\">\n {children}\n </pre>\n ),\n code: ({ children, className }) => {\n // Fenced blocks carry a `language-*` class; inline code has none.\n const isBlock = (className ?? \"\").startsWith(\"language-\");\n if (isBlock) {\n return <code className={className}>{children}</code>;\n }\n return (\n <code className=\"rounded bg-muted px-1 py-0.5 text-[0.8125rem]\">\n {children}\n </code>\n );\n },\n }}\n >\n {text}\n </ReactMarkdown>\n );\n}\n","import {\n ArrowUp,\n Check,\n Maximize2,\n Minimize2,\n Square,\n Trash2,\n X,\n} from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { UseReactorChatResult } from \"../use-reactor-chat.js\";\nimport type { ChatMessage, ChatPart } from \"../types.js\";\nimport { ApprovalCard } from \"./approval-card.js\";\nimport { Markdown } from \"./markdown.js\";\n\nfunction formatArgs(args: unknown): string {\n let json: string;\n try {\n json = JSON.stringify(args);\n } catch {\n return String(args);\n }\n return json.length > 200 ? `${json.slice(0, 200)}…` : json;\n}\n\nfunction ToolCard({ part }: { part: Extract<ChatPart, { type: \"tool\" }> }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div className=\"rounded-lg border border-border bg-background p-2\">\n <button\n type=\"button\"\n onClick={() => setExpanded((e) => !e)}\n className=\"flex w-full items-center gap-2 text-left text-xs text-foreground\"\n >\n {part.state === \"done\" && (\n <Check size={13} className=\"shrink-0 text-info\" />\n )}\n {part.state === \"error\" && (\n <X size={13} className=\"shrink-0 text-destructive\" />\n )}\n {part.state === \"rejected\" && (\n <X size={13} className=\"shrink-0 text-muted-foreground\" />\n )}\n {part.state === \"executing\" && (\n <span className=\"shrink-0 text-muted-foreground\">⋯</span>\n )}\n <span className=\"font-mono text-muted-foreground\">\n {part.name}({formatArgs(part.args)})\n </span>\n </button>\n {expanded && (\n <pre className=\"mt-2 max-h-40 overflow-auto rounded bg-muted p-2 text-xs text-foreground\">\n {JSON.stringify(\n { args: part.args, result: part.result, error: part.error },\n null,\n 2,\n )}\n </pre>\n )}\n </div>\n );\n}\n\nfunction MessageView({\n message,\n onApprove,\n onReject,\n}: {\n message: ChatMessage;\n onApprove: (toolCallId: string) => void;\n onReject: (toolCallId: string) => void;\n}) {\n if (message.role === \"user\") {\n return (\n <div className=\"flex justify-end\">\n <div className=\"max-w-[85%] whitespace-pre-wrap rounded-lg bg-muted px-3 py-2 text-sm text-foreground\">\n {message.parts\n .filter(\n (p): p is Extract<ChatPart, { type: \"text\" }> =>\n p.type === \"text\",\n )\n .map((p) => p.text)\n .join(\"\")}\n </div>\n </div>\n );\n }\n return (\n <div className=\"space-y-2\">\n {message.parts.map((part, index) =>\n part.type === \"text\" ? (\n <div key={index} className=\"text-sm text-foreground\">\n <Markdown text={part.text} />\n </div>\n ) : (\n <div key={part.toolCallId} className=\"space-y-2\">\n <ToolCard part={part} />\n {part.state === \"awaiting-approval\" && (\n <ApprovalCard\n approval={{\n toolCallId: part.toolCallId,\n name: part.name,\n args: part.args,\n }}\n onApprove={onApprove}\n onReject={onReject}\n />\n )}\n </div>\n ),\n )}\n {message.parts.length === 0 && (\n <div className=\"text-sm text-muted-foreground\">Thinking…</div>\n )}\n </div>\n );\n}\n\n/** The chat window panel, anchored above the FAB in the bottom-right. */\nexport function ChatWindow({\n chat,\n onClose,\n}: {\n chat: UseReactorChatResult;\n onClose: () => void;\n}) {\n const [fullscreen, setFullscreen] = useState(false);\n const [draft, setDraft] = useState(\"\");\n const listRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = listRef.current;\n if (el) {\n el.scrollTo({ top: el.scrollHeight });\n }\n }, [chat.messages]);\n\n useEffect(() => {\n if (!fullscreen) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setFullscreen(false);\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [fullscreen]);\n\n const submit = () => {\n const text = draft;\n setDraft(\"\");\n chat.send(text);\n };\n\n return (\n <div\n className={\n fullscreen\n ? \"fixed inset-0 z-50 flex flex-col overflow-hidden bg-background\"\n : \"fixed bottom-24 right-6 z-40 flex max-h-[min(28rem,calc(100vh-8rem))] w-[min(24rem,calc(100vw-3rem))] flex-col overflow-hidden rounded-xl border border-border bg-background shadow-2xl\"\n }\n >\n <div className=\"flex items-center justify-between border-b border-border px-4 py-2.5\">\n <span className=\"text-sm font-semibold text-foreground\">\n AI Assistant\n </span>\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <button\n type=\"button\"\n aria-label={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n title={fullscreen ? \"Exit fullscreen (Esc)\" : \"Fullscreen\"}\n onClick={() => setFullscreen((f) => !f)}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n {fullscreen ? <Minimize2 size={15} /> : <Maximize2 size={15} />}\n </button>\n <button\n type=\"button\"\n aria-label=\"Clear conversation\"\n title=\"New chat\"\n onClick={chat.clear}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n <Trash2 size={15} />\n </button>\n <button\n type=\"button\"\n aria-label=\"Close AI chat\"\n title=\"Close\"\n onClick={onClose}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n <X size={15} />\n </button>\n </div>\n </div>\n\n <div ref={listRef} className=\"flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {chat.messages.length === 0 && (\n <div className=\"pt-8 text-center text-sm text-muted-foreground\">\n {chat.configured ? (\n <p>\n Ask the assistant to create or change documents, manage drives,\n or inspect read models.\n </p>\n ) : (\n <p>\n Add your OpenAI-compatible endpoint under Settings → AI\n Assistant, then start a conversation.\n </p>\n )}\n </div>\n )}\n {chat.messages.map((message) => (\n <MessageView\n key={message.id}\n message={message}\n onApprove={chat.approve}\n onReject={chat.reject}\n />\n ))}\n {chat.error && (\n <div className=\"rounded-lg border border-destructive/40 bg-destructive/10 p-2 text-xs text-destructive\">\n {chat.error}\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border p-3\">\n <div className=\"flex items-end gap-2\">\n <textarea\n rows={2}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n }}\n placeholder={\n chat.configured\n ? \"Ask the assistant… (Enter to send)\"\n : \"Configure the endpoint in settings first\"\n }\n className=\"max-h-32 min-h-[2.5rem] flex-1 resize-none rounded-md border border-border bg-background p-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none\"\n disabled={chat.isStreaming}\n />\n {chat.isStreaming ? (\n <button\n type=\"button\"\n aria-label=\"Stop generating\"\n title=\"Stop\"\n onClick={chat.stop}\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n <Square size={15} />\n </button>\n ) : (\n <button\n type=\"button\"\n aria-label=\"Send message\"\n title=\"Send\"\n onClick={submit}\n disabled={!draft.trim() || !chat.configured}\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:disabled-effect\"\n >\n <ArrowUp size={15} />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import type { AiSettings } from \"./types.js\";\n\nconst STORAGE_KEY = \"ph-ai-chat-settings\";\n\nexport const DEFAULT_AI_SETTINGS: AiSettings = {\n enabled: false,\n baseUrl: \"\",\n apiKey: \"\",\n model: \"\",\n autoApproveWrites: false,\n};\n\ntype Listener = () => void;\n\nlet snapshot: AiSettings = load();\nconst listeners = new Set<Listener>();\n\nfunction load(): AiSettings {\n if (typeof localStorage === \"undefined\") {\n return { ...DEFAULT_AI_SETTINGS };\n }\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) {\n return { ...DEFAULT_AI_SETTINGS };\n }\n const parsed = JSON.parse(raw) as Partial<AiSettings>;\n return {\n enabled: typeof parsed.enabled === \"boolean\" ? parsed.enabled : false,\n baseUrl: typeof parsed.baseUrl === \"string\" ? parsed.baseUrl : \"\",\n apiKey: typeof parsed.apiKey === \"string\" ? parsed.apiKey : \"\",\n model: typeof parsed.model === \"string\" ? parsed.model : \"\",\n autoApproveWrites:\n typeof parsed.autoApproveWrites === \"boolean\"\n ? parsed.autoApproveWrites\n : false,\n };\n } catch {\n return { ...DEFAULT_AI_SETTINGS };\n }\n}\n\nfunction persist(settings: AiSettings): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));\n } catch {\n // Storage unavailable (private mode, quota) — keep the in-memory value.\n }\n}\n\nfunction emit(): void {\n for (const listener of listeners) {\n listener();\n }\n}\n\n/** Returns the current settings (stable snapshot for useSyncExternalStore). */\nexport function getAiSettings(): AiSettings {\n return snapshot;\n}\n\n/** Merges a partial update, persists, and notifies subscribers. */\nexport function updateAiSettings(patch: Partial<AiSettings>): AiSettings {\n snapshot = { ...snapshot, ...patch };\n persist(snapshot);\n emit();\n return snapshot;\n}\n\n/** Clears all stored settings. */\nexport function clearAiSettings(): void {\n snapshot = { ...DEFAULT_AI_SETTINGS };\n try {\n localStorage.removeItem(STORAGE_KEY);\n } catch {\n // ignore\n }\n emit();\n}\n\n/** Subscribes to settings changes. Returns the unsubscribe function. */\nexport function subscribeAiSettings(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** True when the endpoint, key and model are all configured. */\nexport function isAiConfigured(settings: AiSettings): boolean {\n return (\n settings.baseUrl.trim().length > 0 &&\n settings.apiKey.trim().length > 0 &&\n settings.model.trim().length > 0\n );\n}\n","import { DriveCollectionId, type ISyncManager } from \"@powerhousedao/reactor\";\nimport { z } from \"zod\";\nimport { ambientRenownTokenProvider } from \"../graphql-client/auth.js\";\nimport type { AiToolDescriptor } from \"./types.js\";\n\n/**\n * The switchboard serving a remote drive: the base URL the browser talks to\n * and its GraphQL supergraph endpoint.\n */\nexport interface DriveSwitchboard {\n /** Switchboard base URL (e.g. `http://localhost:4001`). */\n switchboardUrl: string;\n /** GraphQL supergraph endpoint (`<base>/graphql`). */\n graphqlUrl: string;\n}\n\n/**\n * Reads the tab-side sync manager from the browser global. Returns\n * `undefined` in non-browser environments or before the reactor client\n * module is initialized.\n */\nexport function getBrowserSyncManager(): ISyncManager | undefined {\n if (typeof window === \"undefined\") {\n return undefined;\n }\n return window.ph?.reactorClientModule?.reactorModule?.syncModule?.syncManager;\n}\n\n/** Suffix of the per-drive GraphQL channel endpoint the switchboard serves. */\nconst GQL_CHANNEL_SUFFIX = \"/graphql/r\";\n\n/**\n * Resolves the switchboard that serves a drive, if any.\n *\n * Every remote drive stores its switchboard's exact GraphQL channel\n * endpoint in its sync remote — `channelConfig = { type: \"gql\",\n * parameters: { url: \"<origin>/graphql/r\" } }` — and the switchboard base\n * is that URL minus the `/graphql/r` suffix. Deriving it per drive (rather\n * than from a global setting) is what keeps multi-switchboard deployments\n * correct. Drives that are not synced to a remote switchboard (local\n * drives, other channel types, unexpected shapes) return `undefined`.\n */\nexport function resolveDriveSwitchboard(\n driveId: string | undefined,\n syncManager?: ISyncManager,\n): DriveSwitchboard | undefined {\n const manager = syncManager ?? getBrowserSyncManager();\n if (!driveId || !manager) {\n return undefined;\n }\n const collectionId = DriveCollectionId.forDrive(driveId);\n const remote = manager\n .list()\n .find((r) => r.meta.collectionId.equals(collectionId));\n if (!remote) {\n return undefined;\n }\n const { channelConfig } = remote.meta;\n if (channelConfig.type !== \"gql\") {\n return undefined;\n }\n const url = channelConfig.parameters.url;\n if (typeof url !== \"string\" || !url.endsWith(GQL_CHANNEL_SUFFIX)) {\n return undefined;\n }\n const switchboardUrl = url.slice(0, -GQL_CHANNEL_SUFFIX.length);\n return { switchboardUrl, graphqlUrl: `${switchboardUrl}/graphql` };\n}\n\n/** A bounded introspected GraphQL type reference (see the query below). */\ntype IntrospectedTypeRef = {\n kind: string;\n name: string | null;\n ofType?: IntrospectedTypeRef | null;\n};\n\ntype IntrospectedField = {\n name: string;\n description: string | null;\n args: Array<{ name: string; type: IntrospectedTypeRef }>;\n type: IntrospectedTypeRef;\n};\n\ntype IntrospectedRootType = {\n fields: IntrospectedField[] | null;\n} | null;\n\ntype IntrospectedSchema = {\n queryType?: IntrospectedRootType;\n mutationType?: IntrospectedRootType;\n};\n\n/**\n * Introspection over the switchboard supergraph's root query and mutation\n * fields. Types are requested with three levels of `ofType` nesting so\n * that common shapes like `[X!]!` (LIST(NON_NULL(LIST(X)))) render fully.\n */\nexport const SWITCHBOARD_INTROSPECTION_QUERY: string = /* GraphQL */ `\n query SwitchboardIntrospection {\n __schema {\n queryType {\n fields {\n name\n description\n args {\n name\n type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n mutationType {\n fields {\n name\n description\n args {\n name\n type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n type {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n ofType {\n kind\n name\n }\n }\n }\n }\n }\n }\n }\n }\n`;\n\n/**\n * Renders an introspected type reference as a GraphQL type string.\n * Recursion is bounded by the `ofType` nesting of the query, so it always\n * terminates.\n */\nfunction renderTypeRef(type: IntrospectedTypeRef | null | undefined): string {\n if (!type) {\n return \"Unknown\";\n }\n switch (type.kind) {\n case \"NON_NULL\":\n return `${renderTypeRef(type.ofType)}!`;\n case \"LIST\":\n return `[${renderTypeRef(type.ofType)}]`;\n default:\n return type.name ?? \"Unknown\";\n }\n}\n\n/** One root query or mutation field, reduced to a prompt-friendly line. */\nexport type SchemaFieldSummary = {\n name: string;\n description?: string;\n args: string;\n returns: string;\n};\n\n/** Cap on the number of fields reported per root type. */\nconst MAX_FIELDS_PER_ROOT = 200;\n\nfunction summarizeFields(fields: IntrospectedField[] | null | undefined): {\n fields: SchemaFieldSummary[];\n truncated: boolean;\n} {\n const list = fields ?? [];\n return {\n fields: list.slice(0, MAX_FIELDS_PER_ROOT).map((field) => ({\n name: field.name,\n ...(field.description ? { description: field.description } : {}),\n args: field.args\n .map((arg) => `${arg.name}: ${renderTypeRef(arg.type)}`)\n .join(\", \"),\n returns: renderTypeRef(field.type),\n })),\n truncated: list.length > MAX_FIELDS_PER_ROOT,\n };\n}\n\n/**\n * Reduces a GraphQL `__schema` introspection payload (the `data.__schema`\n * object) to a compact summary of its root query and mutation fields.\n * Accepts `unknown` because the payload arrives over the wire.\n */\nexport function summarizeIntrospectionSchema(schema: unknown): {\n queries: SchemaFieldSummary[];\n mutations: SchemaFieldSummary[];\n truncated: boolean;\n} {\n const introspection = (schema ?? {}) as IntrospectedSchema;\n const queries = summarizeFields(introspection.queryType?.fields);\n const mutations = summarizeFields(introspection.mutationType?.fields);\n return {\n queries: queries.fields,\n mutations: mutations.fields,\n truncated: queries.truncated || mutations.truncated,\n };\n}\n\nexport const SWITCHBOARD_SCHEMA_TOOL_NAME = \"getSwitchboardSchema\";\n\n/** Test seams for {@link createSwitchboardSchemaTool}. */\nexport interface SwitchboardToolDeps {\n getSyncManager?: () => ISyncManager | undefined;\n getSelectedDriveId?: () => string | undefined;\n tokenProvider?: () => Promise<string | undefined>;\n fetchImpl?: typeof fetch;\n timeoutMs?: number;\n}\n\n/**\n * Creates the read-only `getSwitchboardSchema` tool: it introspects the\n * GraphQL supergraph of the switchboard serving a drive and returns a\n * summary of its root queries and mutations. It reads the schema only and\n * never modifies data.\n */\nexport function createSwitchboardSchemaTool(\n deps?: SwitchboardToolDeps,\n): AiToolDescriptor {\n const getSyncManager = deps?.getSyncManager ?? getBrowserSyncManager;\n const getSelectedDriveId =\n deps?.getSelectedDriveId ??\n ((): string | undefined =>\n typeof window === \"undefined\" ? undefined : window.ph?.selectedDriveId);\n const tokenProvider = deps?.tokenProvider ?? ambientRenownTokenProvider;\n const fetchImpl = deps?.fetchImpl ?? fetch;\n const timeoutMs = deps?.timeoutMs ?? 10_000;\n\n return {\n name: SWITCHBOARD_SCHEMA_TOOL_NAME,\n description:\n \"Introspect the GraphQL endpoint of the switchboard serving a drive and list the queries and mutations it exposes (reactor operations, document-model read models, package subgraphs). Read-only: it reads the schema only and never modifies data. Omit driveId to introspect the currently selected drive.\",\n inputSchema: {\n driveId: z\n .string()\n .describe(\n \"Drive to introspect. Omit to use the currently selected drive.\",\n )\n .optional(),\n },\n annotations: { readOnlyHint: true },\n callback: async (input: { driveId?: string } | undefined) => {\n const driveId = input?.driveId ?? getSelectedDriveId();\n const sb = resolveDriveSwitchboard(driveId, getSyncManager());\n if (!sb) {\n throw new Error(\n driveId\n ? `Drive \"${driveId}\" has no switchboard: it is not synced to a remote switchboard.`\n : \"No drive is currently selected and no driveId was given.\",\n );\n }\n\n const token = await tokenProvider();\n let response: Response;\n try {\n response = await fetchImpl(sb.graphqlUrl, {\n method: \"POST\",\n headers: {\n \"content-type\": \"application/json\",\n ...(token ? { authorization: `Bearer ${token}` } : {}),\n },\n body: JSON.stringify({ query: SWITCHBOARD_INTROSPECTION_QUERY }),\n signal: AbortSignal.timeout(timeoutMs),\n });\n } catch (error) {\n throw new Error(\n `Switchboard at ${sb.graphqlUrl} is not reachable: ${\n error instanceof Error ? error.message : String(error)\n }`,\n { cause: error },\n );\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new Error(\n `Switchboard at ${sb.graphqlUrl} refused the request (HTTP ${response.status}). Sign in to the switchboard (Renown) and try again.`,\n );\n }\n if (!response.ok) {\n throw new Error(\n `Switchboard introspection failed: HTTP ${response.status} ${response.statusText} from ${sb.graphqlUrl}`,\n );\n }\n\n const body = (await response.json()) as {\n errors?: Array<{ message?: string }>;\n data?: { __schema?: unknown } | null;\n } | null;\n const schemaErrors = body?.errors;\n if (schemaErrors && schemaErrors.length > 0) {\n throw new Error(\n schemaErrors.map((e) => e.message ?? \"unknown error\").join(\"; \"),\n );\n }\n const schema = body?.data?.__schema as\n | IntrospectedSchema\n | null\n | undefined;\n if (!schema?.queryType) {\n throw new Error(\n `Switchboard at ${sb.graphqlUrl} did not return a GraphQL schema.`,\n );\n }\n\n const summary = summarizeIntrospectionSchema(schema);\n return {\n switchboardUrl: sb.switchboardUrl,\n graphqlUrl: sb.graphqlUrl,\n ...summary,\n };\n },\n };\n}\n","import { isFileNode, isFolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useSelectedDriveSafe } from \"../hooks/selected-drive.js\";\nimport { useSelectedDocumentId } from \"../hooks/selected-document.js\";\nimport { useSelectedNode } from \"../hooks/selected-node.js\";\nimport { resolveDriveSwitchboard } from \"./switchboard.js\";\nimport type { ChatContext } from \"./types.js\";\n\n/**\n * Snapshot of the current selection in the drive explorer, used to make the\n * chat context-aware: \"create a budget here\" targets the selected drive,\n * \"summarize this document\" targets the selected document.\n */\nexport function useChatContext(): ChatContext {\n const [drive] = useSelectedDriveSafe();\n const node = useSelectedNode();\n const documentId = useSelectedDocumentId();\n\n const context: ChatContext = {\n driveId: drive?.header.id,\n driveName: drive?.header.name,\n };\n\n if (node) {\n context.nodeId = node.id;\n context.nodeName = node.name;\n if (isFolderNode(node)) {\n context.nodeKind = \"folder\";\n } else if (isFileNode(node)) {\n context.nodeKind = \"file\";\n }\n }\n\n if (documentId && node && isFileNode(node)) {\n // The selected file node is a document; the node itself carries the\n // name and document model type.\n context.documentName = node.name;\n context.documentType = node.documentType;\n context.documentId = documentId;\n }\n const sb = resolveDriveSwitchboard(drive?.header.id);\n if (sb) {\n context.switchboardUrl = sb.switchboardUrl;\n context.switchboardGraphqlUrl = sb.graphqlUrl;\n }\n return context;\n}\n","import { useCallback, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ModelMessage } from \"ai\";\nimport { ReactorChatAgent } from \"./agent.js\";\nimport { useChatContext } from \"./context.js\";\nimport {\n getAiSettings,\n isAiConfigured,\n subscribeAiSettings,\n} from \"./settings-store.js\";\nimport type {\n AgentEvent,\n AiSettings,\n AiToolDescriptor,\n AiToolsProvider,\n ChatMessage,\n ChatPart,\n PendingApproval,\n} from \"./types.js\";\n\nfunction updateAssistantPart(\n messages: ChatMessage[],\n assistantId: string | null,\n update: (parts: ChatPart[]) => ChatPart[],\n): ChatMessage[] {\n if (!assistantId) {\n return messages;\n }\n return messages.map((message) =>\n message.id === assistantId\n ? { ...message, parts: update(message.parts) }\n : message,\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport interface UseReactorChatResult {\n messages: ChatMessage[];\n isStreaming: boolean;\n pendingApprovals: PendingApproval[];\n error: string | null;\n settings: AiSettings;\n configured: boolean;\n send: (text: string) => void;\n stop: () => void;\n approve: (toolCallId: string) => void;\n reject: (toolCallId: string) => void;\n clear: () => void;\n}\n\n/**\n * Drives one in-browser chat conversation against the reactor tools.\n *\n * `toolsProvider` lazily resolves the tool descriptors (e.g. from\n * `createReactorMcpProvider` bound to `window.ph.reactorClient`) when the\n * user sends a message, so the reactor does not need to be ready at render\n * time.\n */\nexport function useReactorChat(\n toolsProvider?: AiToolsProvider,\n): UseReactorChatResult {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [isStreaming, setIsStreaming] = useState(false);\n const [pendingApprovals, setPendingApprovals] = useState<PendingApproval[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const agentRef = useRef<ReactorChatAgent | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const streamingRef = useRef(false);\n const pendingRef = useRef<Set<string>>(new Set());\n const assistantIdRef = useRef<string | null>(null);\n const historyRef = useRef<ModelMessage[]>([]);\n\n const settings = useSyncExternalStore(subscribeAiSettings, getAiSettings);\n const context = useChatContext();\n\n const handleEvent = useCallback((event: AgentEvent) => {\n const assistantId = assistantIdRef.current;\n switch (event.type) {\n case \"text-delta\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) => {\n const last = parts[parts.length - 1] as ChatPart | undefined;\n if (last?.type === \"text\") {\n return [\n ...parts.slice(0, -1),\n { ...last, text: last.text + event.delta },\n ];\n }\n return [...parts, { type: \"text\", text: event.delta }];\n }),\n );\n break;\n case \"tool-start\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) => [\n ...parts,\n {\n type: \"tool\",\n toolCallId: event.toolCallId,\n name: event.name,\n args: event.args,\n // The SDK may invoke tool approval before the tool-call part\n // reaches the stream consumer, so approval-request can precede\n // tool-start. Seed the part in the awaiting state when that\n // happens instead of flashing \"executing\".\n state: pendingRef.current.has(event.toolCallId)\n ? \"awaiting-approval\"\n : \"executing\",\n },\n ]),\n );\n break;\n case \"approval-request\":\n pendingRef.current.add(event.toolCallId);\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? { ...part, state: \"awaiting-approval\" }\n : part,\n ),\n ),\n );\n setPendingApprovals((prev) => [\n ...prev,\n {\n toolCallId: event.toolCallId,\n name: event.name,\n args: event.args,\n },\n ]);\n break;\n case \"approval-resolved\":\n pendingRef.current.delete(event.toolCallId);\n setPendingApprovals((prev) =>\n prev.filter((a) => a.toolCallId !== event.toolCallId),\n );\n if (!event.approved) {\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? { ...part, state: \"rejected\" }\n : part,\n ),\n ),\n );\n }\n break;\n case \"tool-result\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? {\n ...part,\n state: event.state,\n result: event.result,\n error: event.error,\n }\n : part,\n ),\n ),\n );\n break;\n case \"error\":\n setError(event.error);\n break;\n case \"finish\":\n break;\n }\n }, []);\n\n const send = useCallback(\n (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || streamingRef.current) {\n return;\n }\n setError(null);\n\n const currentSettings = getAiSettings();\n if (!isAiConfigured(currentSettings)) {\n setError(\n \"Configure the AI endpoint, API key and model in settings first.\",\n );\n return;\n }\n\n void (async () => {\n let tools: AiToolDescriptor[];\n try {\n if (!toolsProvider) {\n throw new Error(\"No tool provider configured\");\n }\n tools = await toolsProvider();\n } catch (toolError) {\n setError(\n `Could not initialise the reactor tools: ${toErrorMessage(toolError)}`,\n );\n return;\n }\n if (tools.length === 0) {\n setError(\"The reactor exposed no tools.\");\n return;\n }\n\n const controller = new AbortController();\n abortRef.current = controller;\n const agent = new ReactorChatAgent({\n settings: currentSettings,\n tools,\n context,\n onEvent: handleEvent,\n signal: controller.signal,\n history: historyRef.current,\n });\n agentRef.current = agent;\n\n const assistantId = crypto.randomUUID();\n assistantIdRef.current = assistantId;\n setMessages((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n role: \"user\",\n parts: [{ type: \"text\", text: trimmed }],\n },\n { id: assistantId, role: \"assistant\", parts: [] },\n ]);\n\n streamingRef.current = true;\n setIsStreaming(true);\n try {\n await agent.send(trimmed);\n } catch (sendError) {\n setError(toErrorMessage(sendError));\n } finally {\n streamingRef.current = false;\n setIsStreaming(false);\n abortRef.current = null;\n historyRef.current = agent.getHistory();\n }\n })();\n },\n [toolsProvider, context, handleEvent],\n );\n\n const stop = useCallback(() => {\n abortRef.current?.abort();\n }, []);\n\n const approve = useCallback((toolCallId: string) => {\n agentRef.current?.approve(toolCallId);\n }, []);\n\n const reject = useCallback((toolCallId: string) => {\n agentRef.current?.reject(toolCallId);\n }, []);\n\n const clear = useCallback(() => {\n abortRef.current?.abort();\n agentRef.current?.reset();\n agentRef.current = null;\n historyRef.current = [];\n pendingRef.current.clear();\n setPendingApprovals([]);\n setError(null);\n assistantIdRef.current = null;\n }, []);\n\n return {\n messages,\n isStreaming,\n pendingApprovals,\n error,\n settings,\n configured: isAiConfigured(settings),\n send,\n stop,\n approve,\n reject,\n clear,\n };\n}\n","import { MessageCircle, X } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\nimport { getAiSettings, subscribeAiSettings } from \"../settings-store.js\";\nimport { useReactorChat } from \"../use-reactor-chat.js\";\nimport type { AiToolsProvider } from \"../types.js\";\nimport { ChatWindow } from \"./chat-window.js\";\n\n/**\n * Bottom-right floating action button that opens the reactor AI chat window.\n *\n * `getTools` lazily resolves the reactor tool descriptors (from\n * `createReactorMcpProvider` bound to the browser reactor client) when the\n * user sends a message.\n */\nexport function ReactorChatFab({ getTools }: { getTools?: AiToolsProvider }) {\n const settings = useSyncExternalStore(subscribeAiSettings, getAiSettings);\n const [open, setOpen] = useState(false);\n const chat = useReactorChat(getTools);\n\n if (!settings.enabled) return null;\n return (\n <>\n <button\n type=\"button\"\n aria-label={open ? \"Close AI chat\" : \"Open AI chat\"}\n title={open ? \"Close AI chat\" : \"AI chat\"}\n onClick={() => setOpen((o) => !o)}\n className=\"fixed bottom-6 right-6 z-40 flex size-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-transform hover:scale-105 disabled:disabled-effect\"\n >\n {open ? <X size={20} /> : <MessageCircle size={20} />}\n </button>\n {open && <ChatWindow chat={chat} onClose={() => setOpen(false)} />}\n </>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["formatArgs"],"sources":["../../../src/ai/types.ts","../../../src/ai/agent.ts","../../../src/ai/components/approval-card.tsx","../../../src/ai/components/markdown.tsx","../../../src/ai/components/chat-window.tsx","../../../src/ai/settings-store.ts","../../../src/ai/context.ts","../../../src/ai/use-reactor-chat.ts","../../../src/ai/components/reactor-chat-fab.tsx"],"sourcesContent":["import type {\n PhAiToolAnnotations,\n PhAiToolDescriptor,\n} from \"@powerhousedao/shared/document-model\";\n\n/**\n * Browser-side AI chat over the reactor.\n *\n * The chat agent is tool-agnostic: consumers (e.g. Connect) pass a lazy\n * provider of {@link AiToolDescriptor}s. The descriptors mirror the\n * provider-agnostic tool core in `@powerhousedao/reactor-mcp/tools`, so the\n * same tool definitions drive both the MCP server and the in-browser chat.\n */\n\n/** User-configured LLM connection settings, persisted in localStorage. */\nexport interface AiSettings {\n /** Master on/off for the in-browser assistant. Off by default. */\n enabled: boolean;\n /** OpenAI-compatible base URL, e.g. `https://api.openai.com/v1` */\n baseUrl: string;\n /** API key. Sent only to the configured endpoint, never to Powerhouse. */\n apiKey: string;\n /** Model id the endpoint serves, e.g. `gpt-4o-mini`. */\n model: string;\n /**\n * When false (default), write tools render an approval card and pause the\n * agent loop until the user approves or rejects the action.\n */\n autoApproveWrites: boolean;\n}\n\n/** MCP-compatible annotation hints; structural, no MCP SDK dependency. */\nexport type AiToolAnnotations = PhAiToolAnnotations;\n\n/**\n * Provider-agnostic tool descriptor accepted by the chat agent.\n *\n * Structurally compatible with the MCP tool records produced by\n * `createReactorMcpProvider`: the callback parameter is `never` so any\n * per-tool-args function type is assignable, and the result is `unknown`\n * because the envelope shape (MCP `CallToolResult`) is unwrapped at the\n * adapter boundary.\n */\nexport type AiToolDescriptor = PhAiToolDescriptor;\n\n/** Tool descriptors the app resolves when the user sends a message. */\nexport type AiToolsProvider = () => Promise<AiToolDescriptor[]>;\n\n/** Tool names whose execution mutates the reactor and requires approval. */\nexport const WRITE_TOOLS: ReadonlySet<string> = new Set([\n \"createDocument\",\n \"addActions\",\n \"deleteDocument\",\n \"addDrive\",\n \"deleteDrive\",\n \"addRemoteDrive\",\n]);\n\n/**\n * Whether executing the tool mutates state and requires user approval:\n * the built-in write tools, or any tool flagged destructive in its\n * annotations (covers package-provided tools outside the built-in set).\n */\nexport function isWriteTool(\n name: string,\n annotations?: AiToolAnnotations,\n): boolean {\n return WRITE_TOOLS.has(name) || annotations?.destructiveHint === true;\n}\n\nexport type ToolCallState =\n | \"awaiting-approval\"\n | \"executing\"\n | \"done\"\n | \"error\"\n | \"rejected\";\n\n/** One rendered unit inside a chat message. */\nexport type ChatPart =\n | { type: \"text\"; text: string }\n | {\n type: \"tool\";\n toolCallId: string;\n name: string;\n args: unknown;\n state: ToolCallState;\n result?: unknown;\n error?: string;\n };\n\nexport interface ChatMessage {\n id: string;\n role: \"user\" | \"assistant\";\n parts: ChatPart[];\n}\n\n/** A write tool call the user must approve or reject. */\nexport interface PendingApproval {\n toolCallId: string;\n name: string;\n args: unknown;\n}\n\n/** Context snapshot injected into the agent system prompt. */\nexport interface ChatContext {\n driveId?: string;\n driveName?: string;\n nodeId?: string;\n nodeName?: string;\n nodeKind?: \"file\" | \"folder\";\n documentType?: string;\n documentName?: string;\n documentId?: string;\n switchboardUrl?: string;\n switchboardGraphqlUrl?: string;\n}\n\n/** Events the agent emits while running a turn. */\nexport type AgentEvent =\n | { type: \"text-delta\"; delta: string }\n | {\n type: \"tool-start\";\n toolCallId: string;\n name: string;\n args: unknown;\n }\n | {\n type: \"approval-request\";\n toolCallId: string;\n name: string;\n args: unknown;\n }\n | { type: \"approval-resolved\"; toolCallId: string; approved: boolean }\n | {\n type: \"tool-result\";\n toolCallId: string;\n name: string;\n state: \"done\" | \"error\" | \"rejected\";\n result?: unknown;\n error?: string;\n }\n /**\n * Token accounting for the turn: the input tokens of the model's last\n * step (i.e. the size of the context it was reasoning over).\n */\n | { type: \"usage\"; inputTokens: number }\n | { type: \"finish\" }\n | { type: \"error\"; error: string };\n","import {\n type LanguageModel,\n type ModelMessage,\n type Tool,\n isStepCount,\n streamText,\n tool,\n} from \"ai\";\nimport { toResponseMessages } from \"ai/internal\";\nimport { createOpenAICompatible } from \"@ai-sdk/openai-compatible\";\nimport { z } from \"zod\";\nimport { isWriteTool } from \"./types.js\";\nimport type {\n AgentEvent,\n AiSettings,\n AiToolDescriptor,\n ChatContext,\n} from \"./types.js\";\n\n/** Hard cap on model round-trips per user message (each tool step counts). */\nexport const MAX_AGENT_STEPS = 10;\n\n/**\n * Hard cap on the characters of one tool result fed back to the model.\n *\n * Large results (whole document states, catalog listings, schema\n * summaries) accumulate across steps because every step resends the whole\n * conversation; without a budget a few broad queries exhaust the model's\n * context and stall the turn. Over-budget results are truncated with a\n * marker so the model re-queries more narrowly.\n */\nexport const MAX_TOOL_RESULT_CHARS = 12_000;\n\nconst TRUNCATION_NOTE =\n \"\\n...[truncated: this tool result exceeded the context budget; re-query with a more specific filter or fewer items]\";\n\nfunction boundedToolResult(value: unknown): unknown {\n const text = resultText(value);\n if (text.length <= MAX_TOOL_RESULT_CHARS) return value;\n return text.slice(0, MAX_TOOL_RESULT_CHARS) + TRUNCATION_NOTE;\n}\n\nfunction resultText(value: unknown): string {\n if (typeof value === \"string\") return value;\n const json = JSON.stringify(value);\n // JSON.stringify yields undefined at runtime for undefined inputs\n // even though its type signature says otherwise.\n return typeof json === \"string\" ? json : \"undefined\";\n}\n\n/**\n * Number of the most recent turns whose tool results are kept in full in\n * the committed history. Tool results are re-derivable (the store is the\n * source of truth), so results older than that window are replaced with\n * one-line stubs: this is what keeps a long conversation from exhausting\n * the model's context.\n */\nexport const FULL_RESULT_TURNS = 2;\n\n/** Tool results smaller than this are cheap to keep and are never stubbed. */\nexport const STUB_MIN_CHARS = 500;\n\nfunction toolResultChars(output: unknown): number {\n const part = output as { type?: string; value?: unknown };\n if (part.type === \"text\" && typeof part.value === \"string\") {\n return part.value.length;\n }\n const target = part.value !== undefined ? part.value : output;\n const json = JSON.stringify(target);\n return typeof json === \"string\" ? json.length : 0;\n}\n\nfunction argDigest(input: unknown): string {\n if (input === undefined) return \"\";\n let text: string;\n try {\n text = JSON.stringify(input);\n } catch {\n // Circular structures (impossible for parsed tool args) get an\n // empty digest rather than \"[object Object]\".\n text = \"\";\n }\n if (text.length <= 80) return text;\n return `${text.slice(0, 77)}…`;\n}\n\n/**\n * Replaces tool results older than the last {@link FULL_RESULT_TURNS}\n * turns with one-line stubs naming the tool and its arguments. User and\n * assistant messages are never touched, and tool calls keep their input,\n * so the model can re-query any stubbed result with the same arguments.\n * Idempotent: stubs are below {@link STUB_MIN_CHARS} and pass through.\n */\nexport function stubStaleToolResults(history: ModelMessage[]): ModelMessage[] {\n // Window start: position of the Nth-from-last user message.\n let users = 0;\n let windowStart = 0;\n for (let i = history.length - 1; i >= 0; i--) {\n if (history[i].role === \"user\") {\n users += 1;\n if (users === FULL_RESULT_TURNS) {\n windowStart = i;\n break;\n }\n }\n }\n if (users < FULL_RESULT_TURNS) return history;\n\n // Argument digests for the stubs, looked up by tool call id.\n const callInputs = new Map<string, unknown>();\n for (const m of history) {\n if (m.role === \"assistant\" && Array.isArray(m.content)) {\n for (const part of m.content) {\n if (part.type === \"tool-call\") {\n callInputs.set(part.toolCallId, part.input);\n }\n }\n }\n }\n\n return history.map((m, i) => {\n if (i >= windowStart || m.role !== \"tool\") return m;\n const content = m.content.map((part) => {\n if (part.type !== \"tool-result\") return part;\n const chars = toolResultChars(part.output);\n if (chars < STUB_MIN_CHARS) return part;\n const stub: { type: \"text\"; value: string } = {\n type: \"text\",\n value: `[result omitted: ${part.toolName} ${argDigest(\n callInputs.get(part.toolCallId),\n )} — ${chars} chars; re-query the tool to refetch]`,\n };\n return { ...part, output: stub };\n });\n const changed = content.some((part, index) => part !== m.content[index]);\n if (!changed) return m;\n return { ...m, content };\n });\n}\n\n/**\n * Creates the chat language model for a user-supplied OpenAI-compatible\n * endpoint. Requests go directly from the browser to the endpoint; the API\n * key is never sent to any Powerhouse server.\n */\n\n/**\n * Resolves the configured base URL. Relative paths (e.g. `/v1`) resolve\n * against the page origin, so single-origin deployments can serve the\n * endpoint behind the same reverse proxy without CORS.\n */\nfunction resolveBaseUrl(raw: string): string {\n const base = raw.trim().replace(/\\/+$/, \"\");\n if (/^https?:\\/\\//i.test(base)) return base;\n if (typeof window === \"undefined\") return base;\n return `${window.location.origin}${base.startsWith(\"/\") ? \"\" : \"/\"}${base}`;\n}\n\nexport function createReactorChatModel(settings: AiSettings): LanguageModel {\n const provider = createOpenAICompatible({\n name: \"reactor-chat\",\n baseURL: resolveBaseUrl(settings.baseUrl),\n apiKey: settings.apiKey,\n });\n return provider.chatModel(settings.model.trim());\n}\n\n/**\n * Unwraps the MCP `CallToolResult` envelope produced by the reactor tool\n * core into a plain value for the AI SDK. Throws for error results so the\n * SDK surfaces them as tool errors in the next model step.\n */\nexport function unwrapToolResult(raw: unknown): unknown {\n if (raw && typeof raw === \"object\") {\n const envelope = raw as {\n isError?: boolean;\n content?: Array<{ type?: string; text?: string }>;\n structuredContent?: unknown;\n };\n if (\n typeof envelope.isError === \"boolean\" ||\n Array.isArray(envelope.content)\n ) {\n if (envelope.isError) {\n const text =\n envelope.content?.find((c) => c.type === \"text\")?.text ??\n \"Unknown tool error\";\n throw new Error(text.replace(/^Error:\\s*/, \"\"));\n }\n if (envelope.structuredContent !== undefined) {\n return envelope.structuredContent;\n }\n const textPart = envelope.content?.find((c) => c.type === \"text\");\n if (textPart?.text !== undefined) {\n try {\n return JSON.parse(textPart.text);\n } catch {\n return textPart.text;\n }\n }\n return null;\n }\n }\n return raw;\n}\n\n/** Builds the system prompt, grounding the agent in the current selection. */\nexport function buildSystemPrompt(context: ChatContext): string {\n const lines: string[] = [\n \"You are an assistant embedded in the Powerhouse Connect drive explorer.\",\n \"You operate on a local-first document store (the reactor) exclusively through the provided tools.\",\n \"Documents are instances of typed document models; drives are collections that group documents and folders.\",\n \"Prefer read-only tools to discover state (document models, drives, documents, relationships) before making changes.\",\n \"Never invent document ids, drive ids, folder ids or document model types — discover them with the read tools first.\",\n \"Never accept secret values (passwords, tokens, API keys) in chat. When a connection or configuration requires a secret, tell the user to enter it in the relevant editor (e.g. the connection editor) and point them to the document. Never ask the user to paste a secret into the chat.\",\n \"When the user refers to 'this', 'here' or 'it' without naming a target, they mean the current selection below; prefer it for create/modify targets.\",\n \"Tool results may be truncated when they are large: if you see a truncation marker, narrow the query (more specific filter, fewer items) instead of retrying the same call.\",\n \"Older tool results in this conversation may be replaced by a short '[result omitted: ...]' stub as the context grows; that is normal housekeeping, not an error. If you still need the data, call the same tool again with the same arguments.\",\n ];\n const selection: string[] = [];\n if (context.driveName) {\n selection.push(\n `The current drive is \"${context.driveName}\"` +\n (context.driveId ? ` (id: ${context.driveId})` : \"\") +\n \".\",\n );\n }\n if (context.nodeKind === \"folder\" && context.nodeName) {\n selection.push(`The current folder is \"${context.nodeName}\".`);\n }\n if (context.documentType) {\n selection.push(\n `The current document is \"${context.documentName ?? \"unnamed\"}\" of type \"${context.documentType}\"` +\n (context.documentId ? ` (id: ${context.documentId})` : \"\") +\n \".\",\n );\n }\n if (context.switchboardUrl) {\n selection.push(\n `The switchboard for this drive is at ${context.switchboardUrl}; its GraphQL endpoint is ${context.switchboardGraphqlUrl ?? \"\"}. Use the getSwitchboardSchema tool to list the queries and mutations it exposes.`,\n );\n } else if (context.driveId) {\n selection.push(\n \"This drive is not synced to a switchboard, so no switchboard endpoints are available for it.\",\n );\n }\n if (selection.length > 0) {\n lines.push(\"Current selection:\", ...selection.map((s) => `- ${s}`));\n } else {\n lines.push(\n \"Nothing is currently selected. When the target of an action is ambiguous, ask the user which drive, folder or document it should apply to.\",\n );\n }\n return lines.join(\"\\n\");\n}\n\nexport interface ReactorChatAgentOptions {\n settings: AiSettings;\n tools: AiToolDescriptor[];\n context: ChatContext;\n onEvent: (event: AgentEvent) => void;\n signal?: AbortSignal;\n /** Test seam: override the model (e.g. a mock language model). */\n model?: LanguageModel;\n /** Prior conversation to continue (see {@link ReactorChatAgent.getHistory}). */\n history?: ModelMessage[];\n}\n\n/**\n * Runs one tool-loop conversation turn against an OpenAI-compatible model.\n *\n * The agent keeps the model message history across turns. Write tools are\n * gated by the AI SDK's tool approval: when auto-approval is off, the\n * pending user decision is bridged through {@link approve}/{@link reject}.\n */\nexport class ReactorChatAgent {\n private history: ModelMessage[];\n private approvals = new Map<string, (approved: boolean) => void>();\n\n constructor(private readonly options: ReactorChatAgentOptions) {\n this.history = options.history ?? [];\n }\n\n /** The model history after the last completed turn, for the next agent. */\n getHistory(): ModelMessage[] {\n return this.history;\n }\n\n /** Clears the conversation history (a fresh chat). */\n reset(): void {\n this.history = [];\n this.cancelPendingApprovals();\n }\n\n /** Approves a pending write tool call, resuming the agent loop. */\n approve(toolCallId: string): void {\n this.resolveApproval(toolCallId, true);\n }\n\n /** Rejects a pending write tool call; the rejection is fed to the model. */\n reject(toolCallId: string): void {\n this.resolveApproval(toolCallId, false);\n }\n\n private resolveApproval(toolCallId: string, approved: boolean): void {\n const resolve = this.approvals.get(toolCallId);\n if (resolve) {\n this.approvals.delete(toolCallId);\n resolve(approved);\n }\n }\n\n private cancelPendingApprovals(): void {\n for (const resolve of this.approvals.values()) {\n resolve(false);\n }\n this.approvals.clear();\n }\n\n /** Runs one user turn: appends the message, streams the model, commits history. */\n async send(text: string): Promise<void> {\n const { onEvent, signal, settings } = this.options;\n const userMessage: ModelMessage = { role: \"user\", content: text };\n const messages: ModelMessage[] = [...this.history, userMessage];\n\n const tools: Record<string, Tool> = {};\n for (const descriptor of this.options.tools) {\n tools[descriptor.name] = tool({\n description: descriptor.description,\n inputSchema: z.object(descriptor.inputSchema),\n execute: async (args: unknown) =>\n boundedToolResult(\n unwrapToolResult(await descriptor.callback(args as never)),\n ),\n });\n }\n\n const model = this.options.model ?? createReactorChatModel(settings);\n\n // Messages produced by this turn: every completed step, converted\n // with the SDK's own toResponseMessages, so the committed history\n // is exactly the shape the model already saw mid-turn.\n const turnMessages: ModelMessage[] = [];\n let lastInputTokens: number | undefined;\n\n const result = streamText({\n model,\n system: buildSystemPrompt(this.options.context),\n messages,\n tools,\n stopWhen: isStepCount(MAX_AGENT_STEPS),\n abortSignal: signal,\n onStepEnd: async (step) => {\n turnMessages.push(\n ...(await toResponseMessages({ content: step.content, tools })),\n );\n if (typeof step.usage.inputTokens === \"number\") {\n lastInputTokens = step.usage.inputTokens;\n }\n },\n toolApproval: async ({ toolCall }) => {\n const descriptor = this.options.tools.find(\n (t) => t.name === toolCall.toolName,\n );\n if (\n settings.autoApproveWrites ||\n !isWriteTool(toolCall.toolName, descriptor?.annotations)\n ) {\n return \"not-applicable\";\n }\n const toolCallId = toolCall.toolCallId;\n onEvent({\n type: \"approval-request\",\n toolCallId,\n name: toolCall.toolName,\n args: toolCall.input,\n });\n const approved = await new Promise<boolean>((resolve) => {\n this.approvals.set(toolCallId, resolve);\n });\n onEvent({ type: \"approval-resolved\", toolCallId, approved });\n return approved\n ? \"approved\"\n : {\n type: \"denied\",\n reason:\n \"The user denied this action. Do not retry it; acknowledge the denial and ask how to proceed.\",\n };\n },\n });\n\n for await (const part of result.fullStream) {\n this.handleStreamPart(part, onEvent);\n }\n\n if (lastInputTokens !== undefined) {\n onEvent({ type: \"usage\", inputTokens: lastInputTokens });\n }\n\n // Commit the turn: the user message plus every completed step,\n // with orphaned tool calls dropped (step cap / abort), and stale\n // tool results stubbed so the context stays bounded across turns.\n this.history = stubStaleToolResults([\n ...this.history,\n userMessage,\n ...this.dropUnresolvedToolCalls(turnMessages),\n ]);\n onEvent({ type: \"finish\" });\n }\n\n /**\n * Removes tool calls that were never executed (step cap or abort):\n * OpenAI-compatible APIs reject a tool call without its tool result.\n */\n private dropUnresolvedToolCalls(messages: ModelMessage[]): ModelMessage[] {\n const resolved = new Set<string>();\n for (const m of messages) {\n if (m.role === \"tool\") {\n for (const part of m.content) {\n if (part.type === \"tool-result\") {\n resolved.add(part.toolCallId);\n }\n }\n }\n }\n const out = [...messages];\n if (out.length === 0) {\n return out;\n }\n const last = out[out.length - 1];\n if (last.role !== \"assistant\" || typeof last.content === \"string\") {\n return out;\n }\n const content = last.content.filter(\n (part) => part.type !== \"tool-call\" || resolved.has(part.toolCallId),\n );\n if (content.length === 0) {\n out.pop();\n } else {\n out[out.length - 1] = { ...last, content };\n }\n return out;\n }\n\n private handleStreamPart(\n part: unknown,\n onEvent: (e: AgentEvent) => void,\n ): void {\n const p = part as { type: string };\n switch (p.type) {\n case \"text-delta\": {\n const { text } = p as { type: \"text-delta\"; text: string };\n onEvent({ type: \"text-delta\", delta: text });\n break;\n }\n case \"tool-call\": {\n const { toolCallId, toolName, input } = p as {\n type: \"tool-call\";\n toolCallId: string;\n toolName: string;\n input: unknown;\n };\n onEvent({\n type: \"tool-start\",\n toolCallId,\n name: toolName,\n args: input,\n });\n break;\n }\n case \"tool-result\": {\n const { toolCallId, toolName, output } = p as {\n type: \"tool-result\";\n toolCallId: string;\n toolName: string;\n output: unknown;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"done\",\n result: output,\n });\n break;\n }\n case \"tool-error\": {\n const { toolCallId, toolName, error } = p as {\n type: \"tool-error\";\n toolCallId: string;\n toolName: string;\n error: unknown;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"error\",\n error: error instanceof Error ? error.message : String(error),\n });\n break;\n }\n case \"tool-approval-response\": {\n const { approved, toolCall } = p as unknown as {\n approved: boolean;\n toolCall: { toolCallId: string; toolName: string };\n };\n if (!approved) {\n onEvent({\n type: \"tool-result\",\n toolCallId: toolCall.toolCallId,\n name: toolCall.toolName,\n state: \"rejected\",\n });\n }\n break;\n }\n case \"tool-output-denied\": {\n const { toolCallId, toolName } = p as {\n type: \"tool-output-denied\";\n toolCallId: string;\n toolName: string;\n };\n onEvent({\n type: \"tool-result\",\n toolCallId,\n name: toolName,\n state: \"rejected\",\n });\n break;\n }\n case \"abort\":\n // The loop ends; the hook observes the aborted controller.\n break;\n case \"error\": {\n const { error } = p as { type: \"error\"; error: unknown };\n onEvent({\n type: \"error\",\n error: error instanceof Error ? error.message : String(error),\n });\n break;\n }\n default:\n break;\n }\n }\n}\n","import { Check, X } from \"lucide-react\";\nimport type { PendingApproval } from \"../types.js\";\n\nfunction formatArgs(args: unknown): string {\n let json: string;\n try {\n json = JSON.stringify(args, null, 2);\n } catch {\n return String(args);\n }\n return json.length > 400 ? `${json.slice(0, 400)}\\n…` : json;\n}\n\n/**\n * In-chat approval card for a write tool call. Approving or rejecting\n * resolves the pending approval inside the agent loop.\n */\nexport function ApprovalCard({\n approval,\n onApprove,\n onReject,\n}: {\n approval: PendingApproval;\n onApprove: (toolCallId: string) => void;\n onReject: (toolCallId: string) => void;\n}) {\n return (\n <div className=\"rounded-lg border border-border bg-muted/40 p-3\">\n <p className=\"mb-1 text-xs font-semibold uppercase tracking-wide text-muted-foreground\">\n Approval required — {approval.name}\n </p>\n <pre className=\"mb-3 max-h-40 overflow-auto rounded bg-background p-2 text-xs text-foreground\">\n {formatArgs(approval.args)}\n </pre>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={() => onApprove(approval.toolCallId)}\n className=\"flex items-center gap-1.5 rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90\"\n >\n <Check size={14} />\n Approve\n </button>\n <button\n type=\"button\"\n onClick={() => onReject(approval.toolCallId)}\n className=\"flex items-center gap-1.5 rounded-md border border-border bg-background px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted\"\n >\n <X size={14} />\n Reject\n </button>\n </div>\n </div>\n );\n}\n","import ReactMarkdown from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\n\n/**\n * Markdown renderer for assistant messages.\n *\n * Uses react-markdown, which builds React elements from the markdown AST and\n * does not execute raw HTML — safe for model-generated content. remark-gfm\n * adds the table/strikethrough/task-list syntax the agent emits. Element\n * styling is scoped to the chat window's text-sm scale via Tailwind\n * utilities, so no extra CSS is needed.\n */\nexport function Markdown({ text }: { text: string }) {\n return (\n <ReactMarkdown\n remarkPlugins={[remarkGfm]}\n components={{\n p: ({ children }) => (\n <p className=\"my-1.5 leading-relaxed first:mt-0 last:mb-0\">\n {children}\n </p>\n ),\n h1: ({ children }) => (\n <h1 className=\"mb-1 mt-3 font-semibold first:mt-0\">{children}</h1>\n ),\n h2: ({ children }) => (\n <h2 className=\"mb-1 mt-3 font-semibold first:mt-0\">{children}</h2>\n ),\n h3: ({ children }) => (\n <h3 className=\"mb-1 mt-2.5 font-semibold first:mt-0\">{children}</h3>\n ),\n ul: ({ children }) => (\n <ul className=\"my-1.5 list-disc space-y-0.5 pl-5\">{children}</ul>\n ),\n ol: ({ children }) => (\n <ol className=\"my-1.5 list-decimal space-y-0.5 pl-5\">{children}</ol>\n ),\n li: ({ children }) => <li className=\"leading-relaxed\">{children}</li>,\n blockquote: ({ children }) => (\n <blockquote className=\"my-1.5 border-l-2 border-border pl-2.5 text-muted-foreground\">\n {children}\n </blockquote>\n ),\n hr: () => <hr className=\"my-2 border-border\" />,\n a: ({ children, href }) => (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noreferrer noopener\"\n className=\"text-primary underline\"\n >\n {children}\n </a>\n ),\n table: ({ children }) => (\n <div className=\"my-1.5 overflow-x-auto\">\n <table className=\"w-full border-collapse text-xs\">{children}</table>\n </div>\n ),\n thead: ({ children }) => <thead>{children}</thead>,\n th: ({ children }) => (\n <th className=\"border border-border bg-muted px-2 py-1 text-left font-semibold\">\n {children}\n </th>\n ),\n td: ({ children }) => (\n <td className=\"border border-border px-2 py-1 align-top\">\n {children}\n </td>\n ),\n pre: ({ children }) => (\n <pre className=\"my-1.5 overflow-x-auto rounded-md bg-muted p-2.5 text-xs leading-relaxed\">\n {children}\n </pre>\n ),\n code: ({ children, className }) => {\n // Fenced blocks carry a `language-*` class; inline code has none.\n const isBlock = (className ?? \"\").startsWith(\"language-\");\n if (isBlock) {\n return <code className={className}>{children}</code>;\n }\n return (\n <code className=\"rounded bg-muted px-1 py-0.5 text-[0.8125rem]\">\n {children}\n </code>\n );\n },\n }}\n >\n {text}\n </ReactMarkdown>\n );\n}\n","import {\n ArrowUp,\n Check,\n Maximize2,\n Minimize2,\n Square,\n Trash2,\n X,\n} from \"lucide-react\";\nimport { useEffect, useRef, useState } from \"react\";\nimport type { UseReactorChatResult } from \"../use-reactor-chat.js\";\nimport type { ChatMessage, ChatPart } from \"../types.js\";\nimport { ApprovalCard } from \"./approval-card.js\";\nimport { Markdown } from \"./markdown.js\";\n\nfunction formatArgs(args: unknown): string {\n let json: string;\n try {\n json = JSON.stringify(args);\n } catch {\n return String(args);\n }\n return json.length > 200 ? `${json.slice(0, 200)}…` : json;\n}\n\nfunction ToolCard({ part }: { part: Extract<ChatPart, { type: \"tool\" }> }) {\n const [expanded, setExpanded] = useState(false);\n return (\n <div className=\"rounded-lg border border-border bg-background p-2\">\n <button\n type=\"button\"\n onClick={() => setExpanded((e) => !e)}\n className=\"flex w-full items-center gap-2 text-left text-xs text-foreground\"\n >\n {part.state === \"done\" && (\n <Check size={13} className=\"shrink-0 text-info\" />\n )}\n {part.state === \"error\" && (\n <X size={13} className=\"shrink-0 text-destructive\" />\n )}\n {part.state === \"rejected\" && (\n <X size={13} className=\"shrink-0 text-muted-foreground\" />\n )}\n {part.state === \"executing\" && (\n <span className=\"shrink-0 text-muted-foreground\">⋯</span>\n )}\n <span className=\"font-mono text-muted-foreground\">\n {part.name}({formatArgs(part.args)})\n </span>\n </button>\n {expanded && (\n <pre className=\"mt-2 max-h-40 overflow-auto rounded bg-muted p-2 text-xs text-foreground\">\n {JSON.stringify(\n { args: part.args, result: part.result, error: part.error },\n null,\n 2,\n )}\n </pre>\n )}\n </div>\n );\n}\n\nfunction MessageView({\n message,\n onApprove,\n onReject,\n}: {\n message: ChatMessage;\n onApprove: (toolCallId: string) => void;\n onReject: (toolCallId: string) => void;\n}) {\n if (message.role === \"user\") {\n return (\n <div className=\"flex justify-end\">\n <div className=\"max-w-[85%] whitespace-pre-wrap rounded-lg bg-muted px-3 py-2 text-sm text-foreground\">\n {message.parts\n .filter(\n (p): p is Extract<ChatPart, { type: \"text\" }> =>\n p.type === \"text\",\n )\n .map((p) => p.text)\n .join(\"\")}\n </div>\n </div>\n );\n }\n return (\n <div className=\"space-y-2\">\n {message.parts.map((part, index) =>\n part.type === \"text\" ? (\n <div key={index} className=\"text-sm text-foreground\">\n <Markdown text={part.text} />\n </div>\n ) : (\n <div key={part.toolCallId} className=\"space-y-2\">\n <ToolCard part={part} />\n {part.state === \"awaiting-approval\" && (\n <ApprovalCard\n approval={{\n toolCallId: part.toolCallId,\n name: part.name,\n args: part.args,\n }}\n onApprove={onApprove}\n onReject={onReject}\n />\n )}\n </div>\n ),\n )}\n {message.parts.length === 0 && (\n <div className=\"text-sm text-muted-foreground\">Thinking…</div>\n )}\n </div>\n );\n}\n\n/** The chat window panel, anchored above the FAB in the bottom-right. */\nexport function ChatWindow({\n chat,\n onClose,\n}: {\n chat: UseReactorChatResult;\n onClose: () => void;\n}) {\n const [fullscreen, setFullscreen] = useState(false);\n const [draft, setDraft] = useState(\"\");\n const listRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = listRef.current;\n if (el) {\n el.scrollTo({ top: el.scrollHeight });\n }\n }, [chat.messages]);\n\n useEffect(() => {\n if (!fullscreen) return;\n const onKey = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setFullscreen(false);\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [fullscreen]);\n\n const submit = () => {\n const text = draft;\n setDraft(\"\");\n chat.send(text);\n };\n\n return (\n <div\n className={\n fullscreen\n ? \"fixed inset-0 z-50 flex flex-col overflow-hidden bg-background\"\n : \"fixed bottom-24 right-6 z-40 flex max-h-[min(28rem,calc(100vh-8rem))] w-[min(24rem,calc(100vw-3rem))] flex-col overflow-hidden rounded-xl border border-border bg-background shadow-2xl\"\n }\n >\n <div className=\"flex items-center justify-between border-b border-border px-4 py-2.5\">\n <span className=\"text-sm font-semibold text-foreground\">\n AI Assistant\n </span>\n <div className=\"flex items-center gap-1 text-muted-foreground\">\n <button\n type=\"button\"\n aria-label={fullscreen ? \"Exit fullscreen\" : \"Fullscreen\"}\n title={fullscreen ? \"Exit fullscreen (Esc)\" : \"Fullscreen\"}\n onClick={() => setFullscreen((f) => !f)}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n {fullscreen ? <Minimize2 size={15} /> : <Maximize2 size={15} />}\n </button>\n <button\n type=\"button\"\n aria-label=\"Clear conversation\"\n title=\"New chat\"\n onClick={chat.clear}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n <Trash2 size={15} />\n </button>\n <button\n type=\"button\"\n aria-label=\"Close AI chat\"\n title=\"Close\"\n onClick={onClose}\n className=\"rounded p-1 hover:bg-muted hover:text-foreground\"\n >\n <X size={15} />\n </button>\n </div>\n </div>\n\n <div ref={listRef} className=\"flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {chat.messages.length === 0 && (\n <div className=\"pt-8 text-center text-sm text-muted-foreground\">\n {chat.configured ? (\n <p>\n Ask the assistant to create or change documents, manage drives,\n or inspect read models.\n </p>\n ) : (\n <p>\n Add your OpenAI-compatible endpoint under Settings → AI\n Assistant, then start a conversation.\n </p>\n )}\n </div>\n )}\n {chat.messages.map((message) => (\n <MessageView\n key={message.id}\n message={message}\n onApprove={chat.approve}\n onReject={chat.reject}\n />\n ))}\n {chat.error && (\n <div className=\"rounded-lg border border-destructive/40 bg-destructive/10 p-2 text-xs text-destructive\">\n {chat.error}\n </div>\n )}\n </div>\n\n <div className=\"border-t border-border p-3\">\n <div className=\"flex items-end gap-2\">\n <textarea\n rows={2}\n value={draft}\n onChange={(e) => setDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n }}\n placeholder={\n chat.configured\n ? \"Ask the assistant… (Enter to send)\"\n : \"Configure the endpoint in settings first\"\n }\n className=\"max-h-32 min-h-[2.5rem] flex-1 resize-none rounded-md border border-border bg-background p-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none\"\n disabled={chat.isStreaming}\n />\n {chat.isStreaming ? (\n <button\n type=\"button\"\n aria-label=\"Stop generating\"\n title=\"Stop\"\n onClick={chat.stop}\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n <Square size={15} />\n </button>\n ) : (\n <button\n type=\"button\"\n aria-label=\"Send message\"\n title=\"Send\"\n onClick={submit}\n disabled={!draft.trim() || !chat.configured}\n className=\"flex size-9 shrink-0 items-center justify-center rounded-md bg-primary text-primary-foreground hover:bg-primary/90 disabled:disabled-effect\"\n >\n <ArrowUp size={15} />\n </button>\n )}\n </div>\n </div>\n </div>\n );\n}\n","import type { AiSettings } from \"./types.js\";\n\nconst STORAGE_KEY = \"ph-ai-chat-settings\";\n\nexport const DEFAULT_AI_SETTINGS: AiSettings = {\n enabled: false,\n baseUrl: \"\",\n apiKey: \"\",\n model: \"\",\n autoApproveWrites: false,\n};\n\ntype Listener = () => void;\n\nlet snapshot: AiSettings = load();\nconst listeners = new Set<Listener>();\n\nfunction load(): AiSettings {\n if (typeof localStorage === \"undefined\") {\n return { ...DEFAULT_AI_SETTINGS };\n }\n try {\n const raw = localStorage.getItem(STORAGE_KEY);\n if (!raw) {\n return { ...DEFAULT_AI_SETTINGS };\n }\n const parsed = JSON.parse(raw) as Partial<AiSettings>;\n return {\n enabled: typeof parsed.enabled === \"boolean\" ? parsed.enabled : false,\n baseUrl: typeof parsed.baseUrl === \"string\" ? parsed.baseUrl : \"\",\n apiKey: typeof parsed.apiKey === \"string\" ? parsed.apiKey : \"\",\n model: typeof parsed.model === \"string\" ? parsed.model : \"\",\n autoApproveWrites:\n typeof parsed.autoApproveWrites === \"boolean\"\n ? parsed.autoApproveWrites\n : false,\n };\n } catch {\n return { ...DEFAULT_AI_SETTINGS };\n }\n}\n\nfunction persist(settings: AiSettings): void {\n try {\n localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));\n } catch {\n // Storage unavailable (private mode, quota) — keep the in-memory value.\n }\n}\n\nfunction emit(): void {\n for (const listener of listeners) {\n listener();\n }\n}\n\n/** Returns the current settings (stable snapshot for useSyncExternalStore). */\nexport function getAiSettings(): AiSettings {\n return snapshot;\n}\n\n/** Merges a partial update, persists, and notifies subscribers. */\nexport function updateAiSettings(patch: Partial<AiSettings>): AiSettings {\n snapshot = { ...snapshot, ...patch };\n persist(snapshot);\n emit();\n return snapshot;\n}\n\n/** Clears all stored settings. */\nexport function clearAiSettings(): void {\n snapshot = { ...DEFAULT_AI_SETTINGS };\n try {\n localStorage.removeItem(STORAGE_KEY);\n } catch {\n // ignore\n }\n emit();\n}\n\n/** Subscribes to settings changes. Returns the unsubscribe function. */\nexport function subscribeAiSettings(listener: Listener): () => void {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n}\n\n/** True when the endpoint, key and model are all configured. */\nexport function isAiConfigured(settings: AiSettings): boolean {\n return (\n settings.baseUrl.trim().length > 0 &&\n settings.apiKey.trim().length > 0 &&\n settings.model.trim().length > 0\n );\n}\n","import { isFileNode, isFolderNode } from \"@powerhousedao/shared/document-drive\";\nimport { useSelectedDriveSafe } from \"../hooks/selected-drive.js\";\nimport { useSelectedDocumentId } from \"../hooks/selected-document.js\";\nimport { useSelectedNode } from \"../hooks/selected-node.js\";\nimport { resolveDriveSwitchboard } from \"./switchboard.js\";\nimport type { ChatContext } from \"./types.js\";\n\n/**\n * Snapshot of the current selection in the drive explorer, used to make the\n * chat context-aware: \"create a budget here\" targets the selected drive,\n * \"summarize this document\" targets the selected document.\n */\nexport function useChatContext(): ChatContext {\n const [drive] = useSelectedDriveSafe();\n const node = useSelectedNode();\n const documentId = useSelectedDocumentId();\n\n const context: ChatContext = {\n driveId: drive?.header.id,\n driveName: drive?.header.name,\n };\n\n if (node) {\n context.nodeId = node.id;\n context.nodeName = node.name;\n if (isFolderNode(node)) {\n context.nodeKind = \"folder\";\n } else if (isFileNode(node)) {\n context.nodeKind = \"file\";\n }\n }\n\n if (documentId && node && isFileNode(node)) {\n // The selected file node is a document; the node itself carries the\n // name and document model type.\n context.documentName = node.name;\n context.documentType = node.documentType;\n context.documentId = documentId;\n }\n const sb = resolveDriveSwitchboard(drive?.header.id);\n if (sb) {\n context.switchboardUrl = sb.switchboardUrl;\n context.switchboardGraphqlUrl = sb.graphqlUrl;\n }\n return context;\n}\n","import { useCallback, useRef, useState, useSyncExternalStore } from \"react\";\nimport type { ModelMessage } from \"ai\";\nimport { ReactorChatAgent } from \"./agent.js\";\nimport { useChatContext } from \"./context.js\";\nimport {\n getAiSettings,\n isAiConfigured,\n subscribeAiSettings,\n} from \"./settings-store.js\";\nimport type {\n AgentEvent,\n AiSettings,\n AiToolDescriptor,\n AiToolsProvider,\n ChatMessage,\n ChatPart,\n PendingApproval,\n} from \"./types.js\";\n\nfunction updateAssistantPart(\n messages: ChatMessage[],\n assistantId: string | null,\n update: (parts: ChatPart[]) => ChatPart[],\n): ChatMessage[] {\n if (!assistantId) {\n return messages;\n }\n return messages.map((message) =>\n message.id === assistantId\n ? { ...message, parts: update(message.parts) }\n : message,\n );\n}\n\nfunction toErrorMessage(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n\nexport interface UseReactorChatResult {\n messages: ChatMessage[];\n isStreaming: boolean;\n pendingApprovals: PendingApproval[];\n error: string | null;\n settings: AiSettings;\n configured: boolean;\n send: (text: string) => void;\n stop: () => void;\n approve: (toolCallId: string) => void;\n reject: (toolCallId: string) => void;\n clear: () => void;\n}\n\n/**\n * Drives one in-browser chat conversation against the reactor tools.\n *\n * `toolsProvider` lazily resolves the tool descriptors (e.g. from\n * `createReactorMcpProvider` bound to `window.ph.reactorClient`) when the\n * user sends a message, so the reactor does not need to be ready at render\n * time.\n */\nexport function useReactorChat(\n toolsProvider?: AiToolsProvider,\n): UseReactorChatResult {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [isStreaming, setIsStreaming] = useState(false);\n const [pendingApprovals, setPendingApprovals] = useState<PendingApproval[]>(\n [],\n );\n const [error, setError] = useState<string | null>(null);\n const agentRef = useRef<ReactorChatAgent | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n const streamingRef = useRef(false);\n const pendingRef = useRef<Set<string>>(new Set());\n const assistantIdRef = useRef<string | null>(null);\n const historyRef = useRef<ModelMessage[]>([]);\n\n const settings = useSyncExternalStore(subscribeAiSettings, getAiSettings);\n const context = useChatContext();\n\n const handleEvent = useCallback((event: AgentEvent) => {\n const assistantId = assistantIdRef.current;\n switch (event.type) {\n case \"text-delta\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) => {\n const last = parts[parts.length - 1] as ChatPart | undefined;\n if (last?.type === \"text\") {\n return [\n ...parts.slice(0, -1),\n { ...last, text: last.text + event.delta },\n ];\n }\n return [...parts, { type: \"text\", text: event.delta }];\n }),\n );\n break;\n case \"tool-start\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) => [\n ...parts,\n {\n type: \"tool\",\n toolCallId: event.toolCallId,\n name: event.name,\n args: event.args,\n // The SDK may invoke tool approval before the tool-call part\n // reaches the stream consumer, so approval-request can precede\n // tool-start. Seed the part in the awaiting state when that\n // happens instead of flashing \"executing\".\n state: pendingRef.current.has(event.toolCallId)\n ? \"awaiting-approval\"\n : \"executing\",\n },\n ]),\n );\n break;\n case \"approval-request\":\n pendingRef.current.add(event.toolCallId);\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? { ...part, state: \"awaiting-approval\" }\n : part,\n ),\n ),\n );\n setPendingApprovals((prev) => [\n ...prev,\n {\n toolCallId: event.toolCallId,\n name: event.name,\n args: event.args,\n },\n ]);\n break;\n case \"approval-resolved\":\n pendingRef.current.delete(event.toolCallId);\n setPendingApprovals((prev) =>\n prev.filter((a) => a.toolCallId !== event.toolCallId),\n );\n if (!event.approved) {\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? { ...part, state: \"rejected\" }\n : part,\n ),\n ),\n );\n }\n break;\n case \"tool-result\":\n setMessages((prev) =>\n updateAssistantPart(prev, assistantId, (parts) =>\n parts.map((part) =>\n part.type === \"tool\" && part.toolCallId === event.toolCallId\n ? {\n ...part,\n state: event.state,\n result: event.result,\n error: event.error,\n }\n : part,\n ),\n ),\n );\n break;\n case \"error\":\n setError(event.error);\n break;\n case \"finish\":\n break;\n }\n }, []);\n\n const send = useCallback(\n (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || streamingRef.current) {\n return;\n }\n setError(null);\n\n const currentSettings = getAiSettings();\n if (!isAiConfigured(currentSettings)) {\n setError(\n \"Configure the AI endpoint, API key and model in settings first.\",\n );\n return;\n }\n\n void (async () => {\n let tools: AiToolDescriptor[];\n try {\n if (!toolsProvider) {\n throw new Error(\"No tool provider configured\");\n }\n tools = await toolsProvider();\n } catch (toolError) {\n setError(\n `Could not initialise the reactor tools: ${toErrorMessage(toolError)}`,\n );\n return;\n }\n if (tools.length === 0) {\n setError(\"The reactor exposed no tools.\");\n return;\n }\n\n const controller = new AbortController();\n abortRef.current = controller;\n const agent = new ReactorChatAgent({\n settings: currentSettings,\n tools,\n context,\n onEvent: handleEvent,\n signal: controller.signal,\n history: historyRef.current,\n });\n agentRef.current = agent;\n\n const assistantId = crypto.randomUUID();\n assistantIdRef.current = assistantId;\n setMessages((prev) => [\n ...prev,\n {\n id: crypto.randomUUID(),\n role: \"user\",\n parts: [{ type: \"text\", text: trimmed }],\n },\n { id: assistantId, role: \"assistant\", parts: [] },\n ]);\n\n streamingRef.current = true;\n setIsStreaming(true);\n try {\n await agent.send(trimmed);\n } catch (sendError) {\n setError(toErrorMessage(sendError));\n } finally {\n streamingRef.current = false;\n setIsStreaming(false);\n abortRef.current = null;\n historyRef.current = agent.getHistory();\n }\n })();\n },\n [toolsProvider, context, handleEvent],\n );\n\n const stop = useCallback(() => {\n abortRef.current?.abort();\n }, []);\n\n const approve = useCallback((toolCallId: string) => {\n agentRef.current?.approve(toolCallId);\n }, []);\n\n const reject = useCallback((toolCallId: string) => {\n agentRef.current?.reject(toolCallId);\n }, []);\n\n const clear = useCallback(() => {\n abortRef.current?.abort();\n agentRef.current?.reset();\n agentRef.current = null;\n historyRef.current = [];\n pendingRef.current.clear();\n setPendingApprovals([]);\n setError(null);\n assistantIdRef.current = null;\n }, []);\n\n return {\n messages,\n isStreaming,\n pendingApprovals,\n error,\n settings,\n configured: isAiConfigured(settings),\n send,\n stop,\n approve,\n reject,\n clear,\n };\n}\n","import { MessageCircle, X } from \"lucide-react\";\nimport { useState, useSyncExternalStore } from \"react\";\nimport { getAiSettings, subscribeAiSettings } from \"../settings-store.js\";\nimport { useReactorChat } from \"../use-reactor-chat.js\";\nimport type { AiToolsProvider } from \"../types.js\";\nimport { ChatWindow } from \"./chat-window.js\";\n\n/**\n * Bottom-right floating action button that opens the reactor AI chat window.\n *\n * `getTools` lazily resolves the reactor tool descriptors (from\n * `createReactorMcpProvider` bound to the browser reactor client) when the\n * user sends a message.\n */\nexport function ReactorChatFab({ getTools }: { getTools?: AiToolsProvider }) {\n const settings = useSyncExternalStore(subscribeAiSettings, getAiSettings);\n const [open, setOpen] = useState(false);\n const chat = useReactorChat(getTools);\n\n if (!settings.enabled) return null;\n return (\n <>\n <button\n type=\"button\"\n aria-label={open ? \"Close AI chat\" : \"Open AI chat\"}\n title={open ? \"Close AI chat\" : \"AI chat\"}\n onClick={() => setOpen((o) => !o)}\n className=\"fixed bottom-6 right-6 z-40 flex size-12 items-center justify-center rounded-full bg-primary text-primary-foreground shadow-lg transition-transform hover:scale-105 disabled:disabled-effect\"\n >\n {open ? <X size={20} /> : <MessageCircle size={20} />}\n </button>\n {open && <ChatWindow chat={chat} onClose={() => setOpen(false)} />}\n </>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { a as setDocumentCache, c as useDocumentSafe, d as useGetDocumentAsync, f as useGetDocuments, h as readPromiseState, l as useDocuments, m as addPromiseState, o as useDocument, p as DocumentCache, r as useDispatch, s as useDocumentCache, u as useGetDocument } from "../../document-by-id-BSZqTN66.js";
|
|
2
|
-
import { H as ambientRenownTokenProvider, U as makeAuthMiddleware, n as useDocumentModelModules, t as useDocumentModelModuleById } from "../../document-model-modules-
|
|
3
|
-
import { $r as makeAuthConnectionParams, J as useAttachmentService, Qr as viewFilterInputFromViewFilter, Xr as GraphQLReactorClient, Zr as isGraphQLReactorClient, ai as StaticPackageManager, ci as prepareSignedActions, di as MutateDocumentWithOperationsDocument, ei as startDocumentChangesSubscription, fi as ReactorOperationFieldsFragmentDoc, hi as revisionMapFromRevisionsList, i as useSwitchboardClient, ii as subgraphUrlFromGraphqlUrl, k as useReactorClient, li as signStampedAction, mi as phDocumentFromMutation, n as GraphQLReactorProvider, ni as SubgraphSdkRegistry, oi as packageFromDocumentModels, pi as phDocumentFromGetDocument, r as ensurePHEventHandlers, ri as describeGraphQLDocument, si as resolveDocumentModelModule, t as useDocumentOperations, ti as subscriptionsUrlFromGraphqlUrl, ui as stampAction, w as setReactorClient } from "../../document-operations-
|
|
2
|
+
import { H as ambientRenownTokenProvider, U as makeAuthMiddleware, n as useDocumentModelModules, t as useDocumentModelModuleById } from "../../document-model-modules-DfQBNGc-.js";
|
|
3
|
+
import { $r as makeAuthConnectionParams, J as useAttachmentService, Qr as viewFilterInputFromViewFilter, Xr as GraphQLReactorClient, Zr as isGraphQLReactorClient, ai as StaticPackageManager, ci as prepareSignedActions, di as MutateDocumentWithOperationsDocument, ei as startDocumentChangesSubscription, fi as ReactorOperationFieldsFragmentDoc, hi as revisionMapFromRevisionsList, i as useSwitchboardClient, ii as subgraphUrlFromGraphqlUrl, k as useReactorClient, li as signStampedAction, mi as phDocumentFromMutation, n as GraphQLReactorProvider, ni as SubgraphSdkRegistry, oi as packageFromDocumentModels, pi as phDocumentFromGetDocument, r as ensurePHEventHandlers, ri as describeGraphQLDocument, si as resolveDocumentModelModule, t as useDocumentOperations, ti as subscriptionsUrlFromGraphqlUrl, ui as stampAction, w as setReactorClient } from "../../document-operations-CBExT76y.js";
|
|
4
4
|
export { DocumentCache, GraphQLReactorClient, GraphQLReactorProvider, MutateDocumentWithOperationsDocument, ReactorOperationFieldsFragmentDoc, StaticPackageManager, SubgraphSdkRegistry, addPromiseState, ambientRenownTokenProvider, describeGraphQLDocument, ensurePHEventHandlers, isGraphQLReactorClient, makeAuthConnectionParams, makeAuthMiddleware, packageFromDocumentModels, phDocumentFromGetDocument, phDocumentFromMutation, prepareSignedActions, readPromiseState, resolveDocumentModelModule, revisionMapFromRevisionsList, setDocumentCache, setReactorClient, signStampedAction, stampAction, startDocumentChangesSubscription, subgraphUrlFromGraphqlUrl, subscriptionsUrlFromGraphqlUrl, useAttachmentService, useDispatch, useDocument, useDocumentCache, useDocumentModelModuleById, useDocumentModelModules, useDocumentOperations, useDocumentSafe, useDocuments, useGetDocument, useGetDocumentAsync, useGetDocuments, useReactorClient, useSwitchboardClient, viewFilterInputFromViewFilter };
|