@braedonsaunders/appkit-ai 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,35 @@ application stage through `emptyContent`. Consecutive tool calls collapse into
17
17
  one quiet activity line showing the newest action and total step count; operators can expand it to
18
18
  inspect every input and result without letting a long run overwhelm the thread.
19
19
 
20
+ Applications with durable session dispatch can pass `dispatchState` and an
21
+ ordered `queuedMessages` collection. While work is running, recovering, or
22
+ already queued, new composer submissions go through the app-owned `enqueue`
23
+ callback instead of bypassing the queue. AppKit presents queue position and
24
+ lifecycle state and exposes optional edit, remove, and retry actions; the host
25
+ continues to own persistence, authorization, FIFO claiming, idempotency, and
26
+ recovery.
27
+
28
+ ```tsx
29
+ <AgentPanel
30
+ enabled
31
+ dispatchState="running"
32
+ queuedMessages={queue.map((entry) => ({
33
+ id: entry.id,
34
+ text: entry.prompt,
35
+ position: entry.position,
36
+ status: entry.status,
37
+ editable: entry.status === 'queued',
38
+ removable: entry.status === 'queued',
39
+ retryable: entry.status === 'failed',
40
+ }))}
41
+ enqueue={persistQueuedTurn}
42
+ onEditQueuedMessage={openQueueEditor}
43
+ onRemoveQueuedMessage={removeQueuedTurn}
44
+ onRetryQueuedMessage={retryQueuedTurn}
45
+ send={startTurn}
46
+ />
47
+ ```
48
+
20
49
  ```ts
21
50
  import { runAgentTurn } from '@braedonsaunders/appkit-ai'
22
51
 
package/client.js CHANGED
@@ -4,7 +4,7 @@ import { createAnthropic } from '@ai-sdk/anthropic';
4
4
  import { createGoogleGenerativeAI } from '@ai-sdk/google';
5
5
  import { createOpenAI } from '@ai-sdk/openai';
6
6
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
7
- import { resolvePublicHost, secureFetch, validateOutboundRequestConfiguration, } from '@braedonsaunders/appkit-sync/egress';
7
+ import { resolvePublicHost, secureFetch, validateOutboundRequestConfiguration, } from '@braedonsaunders/appkit-egress-proxy/secure-fetch';
8
8
  import { generateText } from 'ai';
9
9
  const MAX_AI_REQUEST_BYTES = 16 * 1024 * 1024;
10
10
  const MAX_AI_RESPONSE_BYTES = 16 * 1024 * 1024;
package/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["// Config-driven AI client. Provider + API key + models (+ base URL) are passed\n// in per call (resolved from per-tenant settings), NOT read from the environment.\n\nimport { createAnthropic } from '@ai-sdk/anthropic'\nimport { createGoogleGenerativeAI } from '@ai-sdk/google'\nimport { createOpenAI } from '@ai-sdk/openai'\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible'\nimport {\n resolvePublicHost,\n secureFetch,\n validateOutboundRequestConfiguration,\n} from '@braedonsaunders/appkit-sync/egress'\nimport { generateText, type LanguageModel } from 'ai'\n\nconst MAX_AI_REQUEST_BYTES = 16 * 1024 * 1024\nconst MAX_AI_RESPONSE_BYTES = 16 * 1024 * 1024\n/**\n * How long one model call may take before the transport gives up on it.\n *\n * This is not a budget — the caller's own loop governs how much work an agent\n * may do — it is only the point past which a socket is assumed dead. Two\n * minutes turned out to be inside the normal range for a reasoning model\n * answering with tool calls: measured runs on several hosted models spent\n * longer than that on a single step, and each one died on this timeout having\n * produced nothing, billed nothing, and lost every step that came before it.\n *\n * Ten minutes is comfortably past the slowest observed step while still\n * catching a connection that has genuinely gone away.\n */\nconst AI_REQUEST_TIMEOUT_MS = 600_000\n\nexport type AiProvider =\n | 'anthropic'\n | 'openai'\n | 'google'\n | 'openrouter'\n | 'groq'\n | 'xai'\n | 'deepseek'\n | 'mistral'\n | 'custom'\n\nexport type ModelTier = 'fast' | 'smart'\n\n/**\n * Platform-wide AI policy governing per-tenant overrides (mirrors the email/SMS\n * policy modes). 'disabled' is a global kill switch; 'global_only' forces the\n * platform provider for every tenant; 'tenant_optional' lets each tenant use its\n * own provider and falls back to the platform default.\n */\nexport type AiPolicyMode = 'tenant_optional' | 'global_only' | 'disabled'\n\nexport type AiConfig = {\n provider: AiProvider\n apiKey: string\n modelFast?: string | null\n modelSmart?: string | null\n /**\n * Endpoint for OpenAI-compatible providers. Required for `custom`; for the\n * named compatible providers it is an optional override of the built-in URL.\n */\n baseUrl?: string | null\n /**\n * Organization (tenant) identity for prompt grounding, so generated content\n * uses the real org name instead of a placeholder. Populated by\n * `getTenantAiConfig`; content-generation paths inject it into the system\n * prompt, analysis/vision paths ignore it.\n */\n org?: { name: string } | null\n}\n\n// How a provider's language model is constructed. The OpenAI-compatible kind\n// covers OpenRouter, Groq, xAI, DeepSeek, Mistral and any user `custom` endpoint.\ntype ProviderKind = 'anthropic' | 'openai' | 'google' | 'openai-compatible'\n\nexport type ProviderSpec = {\n value: AiProvider\n label: string\n kind: ProviderKind\n /** Built-in endpoint for a named OpenAI-compatible provider (null otherwise). */\n baseUrl: string | null\n /** True when the tenant MUST supply their own base URL (i.e. `custom`). */\n requiresBaseUrl: boolean\n /** Default fast/smart model ids (placeholders + fallbacks). Empty for `custom`. */\n fast: string\n smart: string\n /** Placeholder shown in the API-key field. */\n keyHint: string\n /** Optional note about the model-id format for this provider. */\n modelHint?: string\n /**\n * True when this provider's API accepts IMAGE content inside a tool result\n * (Anthropic does; OpenAI's and Google's function/tool results are text/JSON\n * only). Gates vision tools that return rendered page images to the model —\n * see `providerSupportsImageToolResults`.\n */\n visionToolResults: boolean\n}\n\n/**\n * Provider catalogue — single source of truth for the settings UI, the model\n * factory and config validation. Add a provider here and it lights up everywhere.\n */\nexport const AI_PROVIDER_SPECS: ProviderSpec[] = [\n {\n value: 'anthropic',\n label: 'Anthropic — Claude',\n kind: 'anthropic',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'claude-haiku-4-5-20251001',\n smart: 'claude-sonnet-4-6',\n keyHint: 'sk-ant-…',\n visionToolResults: true,\n },\n {\n value: 'openai',\n label: 'OpenAI — GPT',\n kind: 'openai',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'gpt-4o-mini',\n smart: 'gpt-4o',\n keyHint: 'sk-…',\n visionToolResults: false,\n },\n {\n value: 'google',\n label: 'Google — Gemini',\n kind: 'google',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'gemini-2.5-flash',\n smart: 'gemini-2.5-pro',\n keyHint: 'AIza…',\n visionToolResults: false,\n },\n {\n value: 'openrouter',\n label: 'OpenRouter — any model, one key',\n kind: 'openai-compatible',\n baseUrl: 'https://openrouter.ai/api/v1',\n requiresBaseUrl: false,\n fast: 'anthropic/claude-3.5-haiku',\n smart: 'anthropic/claude-3.5-sonnet',\n keyHint: 'sk-or-…',\n modelHint: 'Use vendor/model slugs, e.g. anthropic/claude-3.5-sonnet or openai/gpt-4o.',\n // OpenRouter proxies many vendors over an OpenAI-compatible surface; image\n // tool-results aren't reliably supported, so keep vision tools off here.\n visionToolResults: false,\n },\n {\n value: 'groq',\n label: 'Groq — fast inference',\n kind: 'openai-compatible',\n baseUrl: 'https://api.groq.com/openai/v1',\n requiresBaseUrl: false,\n fast: 'llama-3.1-8b-instant',\n smart: 'llama-3.3-70b-versatile',\n keyHint: 'gsk_…',\n visionToolResults: false,\n },\n {\n value: 'xai',\n label: 'xAI — Grok',\n kind: 'openai-compatible',\n baseUrl: 'https://api.x.ai/v1',\n requiresBaseUrl: false,\n fast: 'grok-2-1212',\n smart: 'grok-2-vision-1212',\n keyHint: 'xai-…',\n visionToolResults: false,\n },\n {\n value: 'deepseek',\n label: 'DeepSeek',\n kind: 'openai-compatible',\n baseUrl: 'https://api.deepseek.com',\n requiresBaseUrl: false,\n fast: 'deepseek-chat',\n smart: 'deepseek-chat',\n keyHint: 'sk-…',\n visionToolResults: false,\n },\n {\n value: 'mistral',\n label: 'Mistral',\n kind: 'openai-compatible',\n baseUrl: 'https://api.mistral.ai/v1',\n requiresBaseUrl: false,\n fast: 'mistral-small-latest',\n smart: 'mistral-large-latest',\n keyHint: 'Your Mistral API key',\n visionToolResults: false,\n },\n {\n value: 'custom',\n label: 'Custom (OpenAI-compatible)',\n kind: 'openai-compatible',\n baseUrl: null,\n requiresBaseUrl: true,\n fast: '',\n smart: '',\n keyHint: 'Your provider API key',\n modelHint:\n 'Use a public HTTPS OpenAI-compatible endpoint (for example Together, Fireworks, Perplexity, or hosted vLLM) and set explicit model ids.',\n visionToolResults: false,\n },\n]\n\nconst SPEC_BY_VALUE = Object.fromEntries(AI_PROVIDER_SPECS.map((s) => [s.value, s])) as Record<\n AiProvider,\n ProviderSpec\n>\n\nexport function isAiProvider(value: unknown): value is AiProvider {\n return typeof value === 'string' && Object.hasOwn(SPEC_BY_VALUE, value)\n}\n\nexport function providerSpec(provider: AiProvider): ProviderSpec {\n return SPEC_BY_VALUE[provider]\n}\n\nfunction withoutTrailingSlashes(value: string): string {\n let end = value.length\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1\n return value.slice(0, end)\n}\n\n/**\n * Validate and canonicalize a persisted AI endpoint override. Runtime requests\n * repeat the public-DNS check immediately before opening each socket.\n */\nexport async function validateAiBaseUrl(\n provider: AiProvider,\n rawBaseUrl: string | null | undefined,\n): Promise<string | null> {\n const spec = providerSpec(provider)\n const raw = rawBaseUrl?.trim() ?? ''\n if (spec.kind !== 'openai-compatible') {\n if (raw) throw new Error(`${spec.label} does not support a custom base URL.`)\n return null\n }\n if (!raw) {\n if (spec.requiresBaseUrl) throw new Error('A public HTTPS base URL is required.')\n return null\n }\n\n let submitted: URL\n try {\n submitted = new URL(raw)\n } catch {\n throw new Error('The AI base URL is not valid.')\n }\n if (submitted.search || submitted.hash) {\n throw new Error('The AI base URL must not include a query string or fragment.')\n }\n const validated = validateOutboundRequestConfiguration(submitted).url\n await resolvePublicHost(validated.hostname)\n\n const path = withoutTrailingSlashes(validated.pathname)\n validated.pathname = path || '/'\n const canonical = withoutTrailingSlashes(validated.href)\n return canonical || validated.origin\n}\n\nasync function readBoundedRequestBody(request: Request): Promise<Uint8Array | undefined> {\n if (!request.body) return undefined\n const declaredLength = Number(request.headers.get('content-length'))\n if (Number.isFinite(declaredLength) && declaredLength > MAX_AI_REQUEST_BYTES) {\n throw new Error(`AI request body exceeded ${MAX_AI_REQUEST_BYTES} bytes.`)\n }\n\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n try {\n for (;;) {\n if (request.signal.aborted) throw request.signal.reason\n const { done, value } = await reader.read()\n if (done) break\n total += value.byteLength\n if (total > MAX_AI_REQUEST_BYTES) {\n await reader.cancel()\n throw new Error(`AI request body exceeded ${MAX_AI_REQUEST_BYTES} bytes.`)\n }\n chunks.push(value)\n }\n } finally {\n reader.releaseLock()\n }\n\n const body = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n body.set(chunk, offset)\n offset += chunk.byteLength\n }\n return body\n}\n\n/** Socket-pinned transport for tenant-configurable OpenAI-compatible endpoints. */\nexport const secureAiFetch: typeof globalThis.fetch = async (input, init) => {\n const request = new Request(input, init)\n const method = request.method.toUpperCase()\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n throw new Error(`AI request method ${method} is not supported.`)\n }\n const body = await readBoundedRequestBody(request)\n return secureFetch(request.url, {\n method: method as 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n headers: request.headers,\n body,\n timeoutMs: AI_REQUEST_TIMEOUT_MS,\n maxRequestBytes: MAX_AI_REQUEST_BYTES,\n maxResponseBytes: MAX_AI_RESPONSE_BYTES,\n maxRedirects: 2,\n signal: request.signal,\n })\n}\n\nexport function defaultModel(provider: AiProvider, tier: ModelTier): string {\n const spec = SPEC_BY_VALUE[provider] ?? SPEC_BY_VALUE.anthropic\n return tier === 'smart' ? spec.smart : spec.fast\n}\n\n/**\n * Whether the configured provider accepts IMAGE content in a tool result — the\n * capability that lets a tool hand rendered PDF pages back to the model for\n * vision reading. Currently Anthropic only; other providers' tool/function\n * results are text/JSON only, so exposing such a tool to them would break the\n * agent turn. Used to gate the assistant's `view_document_pages` tool.\n */\nexport function providerSupportsImageToolResults(config: AiConfig | null | undefined): boolean {\n if (!config) return false\n return SPEC_BY_VALUE[config.provider]?.visionToolResults ?? false\n}\n\nexport function isAiConfigured(config: AiConfig | null | undefined): config is AiConfig {\n if (!config || !config.apiKey) return false\n const spec = SPEC_BY_VALUE[config.provider]\n if (!spec) return false\n if (spec.requiresBaseUrl && !config.baseUrl?.trim()) return false\n return true\n}\n\n/** Resolve a language model from a tenant's config, or null when not configured. */\nexport function getModel(\n config: AiConfig | null | undefined,\n tier: ModelTier = 'fast',\n): LanguageModel | null {\n if (!isAiConfigured(config)) return null\n const spec = SPEC_BY_VALUE[config.provider]\n const modelId =\n (tier === 'smart' ? config.modelSmart : config.modelFast) || defaultModel(config.provider, tier)\n // `custom` has no default model — without an explicit one, AI stays disabled.\n if (!modelId) return null\n\n switch (spec.kind) {\n case 'anthropic':\n return createAnthropic({ apiKey: config.apiKey })(modelId)\n case 'openai':\n return createOpenAI({ apiKey: config.apiKey })(modelId)\n case 'google':\n return createGoogleGenerativeAI({ apiKey: config.apiKey })(modelId)\n case 'openai-compatible': {\n const baseURL = config.baseUrl?.trim() || spec.baseUrl\n if (!baseURL) return null\n return createOpenAICompatible({\n name: spec.value,\n apiKey: config.apiKey,\n baseURL,\n fetch: secureAiFetch,\n })(modelId)\n }\n }\n}\n\nexport class AIDisabledError extends Error {\n override readonly name = 'AIDisabledError'\n constructor() {\n super('No AI provider is available for this request.')\n }\n}\n\n/** Live test of a config — sends a tiny prompt and reports success/failure. */\nexport async function pingModel(\n config: AiConfig | null | undefined,\n): Promise<{ ok: boolean; message: string }> {\n const model = getModel(config, 'fast')\n if (!model) {\n return {\n ok: false,\n message: 'Not configured yet — check the provider, API key, model and base URL.',\n }\n }\n try {\n const { text } = await generateText({ model, prompt: 'Reply with the single word: ok' })\n return { ok: true, message: `Connected — the model replied “${text.trim().slice(0, 24)}”.` }\n } catch (e) {\n return { ok: false, message: e instanceof Error ? e.message.slice(0, 180) : 'Request failed.' }\n }\n}\n"]}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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","sourcesContent":["// Config-driven AI client. Provider + API key + models (+ base URL) are passed\n// in per call (resolved from per-tenant settings), NOT read from the environment.\n\nimport { createAnthropic } from '@ai-sdk/anthropic'\nimport { createGoogleGenerativeAI } from '@ai-sdk/google'\nimport { createOpenAI } from '@ai-sdk/openai'\nimport { createOpenAICompatible } from '@ai-sdk/openai-compatible'\nimport {\n resolvePublicHost,\n secureFetch,\n validateOutboundRequestConfiguration,\n} from '@braedonsaunders/appkit-egress-proxy/secure-fetch'\nimport { generateText, type LanguageModel } from 'ai'\n\nconst MAX_AI_REQUEST_BYTES = 16 * 1024 * 1024\nconst MAX_AI_RESPONSE_BYTES = 16 * 1024 * 1024\n/**\n * How long one model call may take before the transport gives up on it.\n *\n * This is not a budget — the caller's own loop governs how much work an agent\n * may do — it is only the point past which a socket is assumed dead. Two\n * minutes turned out to be inside the normal range for a reasoning model\n * answering with tool calls: measured runs on several hosted models spent\n * longer than that on a single step, and each one died on this timeout having\n * produced nothing, billed nothing, and lost every step that came before it.\n *\n * Ten minutes is comfortably past the slowest observed step while still\n * catching a connection that has genuinely gone away.\n */\nconst AI_REQUEST_TIMEOUT_MS = 600_000\n\nexport type AiProvider =\n | 'anthropic'\n | 'openai'\n | 'google'\n | 'openrouter'\n | 'groq'\n | 'xai'\n | 'deepseek'\n | 'mistral'\n | 'custom'\n\nexport type ModelTier = 'fast' | 'smart'\n\n/**\n * Platform-wide AI policy governing per-tenant overrides (mirrors the email/SMS\n * policy modes). 'disabled' is a global kill switch; 'global_only' forces the\n * platform provider for every tenant; 'tenant_optional' lets each tenant use its\n * own provider and falls back to the platform default.\n */\nexport type AiPolicyMode = 'tenant_optional' | 'global_only' | 'disabled'\n\nexport type AiConfig = {\n provider: AiProvider\n apiKey: string\n modelFast?: string | null\n modelSmart?: string | null\n /**\n * Endpoint for OpenAI-compatible providers. Required for `custom`; for the\n * named compatible providers it is an optional override of the built-in URL.\n */\n baseUrl?: string | null\n /**\n * Organization (tenant) identity for prompt grounding, so generated content\n * uses the real org name instead of a placeholder. Populated by\n * `getTenantAiConfig`; content-generation paths inject it into the system\n * prompt, analysis/vision paths ignore it.\n */\n org?: { name: string } | null\n}\n\n// How a provider's language model is constructed. The OpenAI-compatible kind\n// covers OpenRouter, Groq, xAI, DeepSeek, Mistral and any user `custom` endpoint.\ntype ProviderKind = 'anthropic' | 'openai' | 'google' | 'openai-compatible'\n\nexport type ProviderSpec = {\n value: AiProvider\n label: string\n kind: ProviderKind\n /** Built-in endpoint for a named OpenAI-compatible provider (null otherwise). */\n baseUrl: string | null\n /** True when the tenant MUST supply their own base URL (i.e. `custom`). */\n requiresBaseUrl: boolean\n /** Default fast/smart model ids (placeholders + fallbacks). Empty for `custom`. */\n fast: string\n smart: string\n /** Placeholder shown in the API-key field. */\n keyHint: string\n /** Optional note about the model-id format for this provider. */\n modelHint?: string\n /**\n * True when this provider's API accepts IMAGE content inside a tool result\n * (Anthropic does; OpenAI's and Google's function/tool results are text/JSON\n * only). Gates vision tools that return rendered page images to the model —\n * see `providerSupportsImageToolResults`.\n */\n visionToolResults: boolean\n}\n\n/**\n * Provider catalogue — single source of truth for the settings UI, the model\n * factory and config validation. Add a provider here and it lights up everywhere.\n */\nexport const AI_PROVIDER_SPECS: ProviderSpec[] = [\n {\n value: 'anthropic',\n label: 'Anthropic — Claude',\n kind: 'anthropic',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'claude-haiku-4-5-20251001',\n smart: 'claude-sonnet-4-6',\n keyHint: 'sk-ant-…',\n visionToolResults: true,\n },\n {\n value: 'openai',\n label: 'OpenAI — GPT',\n kind: 'openai',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'gpt-4o-mini',\n smart: 'gpt-4o',\n keyHint: 'sk-…',\n visionToolResults: false,\n },\n {\n value: 'google',\n label: 'Google — Gemini',\n kind: 'google',\n baseUrl: null,\n requiresBaseUrl: false,\n fast: 'gemini-2.5-flash',\n smart: 'gemini-2.5-pro',\n keyHint: 'AIza…',\n visionToolResults: false,\n },\n {\n value: 'openrouter',\n label: 'OpenRouter — any model, one key',\n kind: 'openai-compatible',\n baseUrl: 'https://openrouter.ai/api/v1',\n requiresBaseUrl: false,\n fast: 'anthropic/claude-3.5-haiku',\n smart: 'anthropic/claude-3.5-sonnet',\n keyHint: 'sk-or-…',\n modelHint: 'Use vendor/model slugs, e.g. anthropic/claude-3.5-sonnet or openai/gpt-4o.',\n // OpenRouter proxies many vendors over an OpenAI-compatible surface; image\n // tool-results aren't reliably supported, so keep vision tools off here.\n visionToolResults: false,\n },\n {\n value: 'groq',\n label: 'Groq — fast inference',\n kind: 'openai-compatible',\n baseUrl: 'https://api.groq.com/openai/v1',\n requiresBaseUrl: false,\n fast: 'llama-3.1-8b-instant',\n smart: 'llama-3.3-70b-versatile',\n keyHint: 'gsk_…',\n visionToolResults: false,\n },\n {\n value: 'xai',\n label: 'xAI — Grok',\n kind: 'openai-compatible',\n baseUrl: 'https://api.x.ai/v1',\n requiresBaseUrl: false,\n fast: 'grok-2-1212',\n smart: 'grok-2-vision-1212',\n keyHint: 'xai-…',\n visionToolResults: false,\n },\n {\n value: 'deepseek',\n label: 'DeepSeek',\n kind: 'openai-compatible',\n baseUrl: 'https://api.deepseek.com',\n requiresBaseUrl: false,\n fast: 'deepseek-chat',\n smart: 'deepseek-chat',\n keyHint: 'sk-…',\n visionToolResults: false,\n },\n {\n value: 'mistral',\n label: 'Mistral',\n kind: 'openai-compatible',\n baseUrl: 'https://api.mistral.ai/v1',\n requiresBaseUrl: false,\n fast: 'mistral-small-latest',\n smart: 'mistral-large-latest',\n keyHint: 'Your Mistral API key',\n visionToolResults: false,\n },\n {\n value: 'custom',\n label: 'Custom (OpenAI-compatible)',\n kind: 'openai-compatible',\n baseUrl: null,\n requiresBaseUrl: true,\n fast: '',\n smart: '',\n keyHint: 'Your provider API key',\n modelHint:\n 'Use a public HTTPS OpenAI-compatible endpoint (for example Together, Fireworks, Perplexity, or hosted vLLM) and set explicit model ids.',\n visionToolResults: false,\n },\n]\n\nconst SPEC_BY_VALUE = Object.fromEntries(AI_PROVIDER_SPECS.map((s) => [s.value, s])) as Record<\n AiProvider,\n ProviderSpec\n>\n\nexport function isAiProvider(value: unknown): value is AiProvider {\n return typeof value === 'string' && Object.hasOwn(SPEC_BY_VALUE, value)\n}\n\nexport function providerSpec(provider: AiProvider): ProviderSpec {\n return SPEC_BY_VALUE[provider]\n}\n\nfunction withoutTrailingSlashes(value: string): string {\n let end = value.length\n while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1\n return value.slice(0, end)\n}\n\n/**\n * Validate and canonicalize a persisted AI endpoint override. Runtime requests\n * repeat the public-DNS check immediately before opening each socket.\n */\nexport async function validateAiBaseUrl(\n provider: AiProvider,\n rawBaseUrl: string | null | undefined,\n): Promise<string | null> {\n const spec = providerSpec(provider)\n const raw = rawBaseUrl?.trim() ?? ''\n if (spec.kind !== 'openai-compatible') {\n if (raw) throw new Error(`${spec.label} does not support a custom base URL.`)\n return null\n }\n if (!raw) {\n if (spec.requiresBaseUrl) throw new Error('A public HTTPS base URL is required.')\n return null\n }\n\n let submitted: URL\n try {\n submitted = new URL(raw)\n } catch {\n throw new Error('The AI base URL is not valid.')\n }\n if (submitted.search || submitted.hash) {\n throw new Error('The AI base URL must not include a query string or fragment.')\n }\n const validated = validateOutboundRequestConfiguration(submitted).url\n await resolvePublicHost(validated.hostname)\n\n const path = withoutTrailingSlashes(validated.pathname)\n validated.pathname = path || '/'\n const canonical = withoutTrailingSlashes(validated.href)\n return canonical || validated.origin\n}\n\nasync function readBoundedRequestBody(request: Request): Promise<Uint8Array | undefined> {\n if (!request.body) return undefined\n const declaredLength = Number(request.headers.get('content-length'))\n if (Number.isFinite(declaredLength) && declaredLength > MAX_AI_REQUEST_BYTES) {\n throw new Error(`AI request body exceeded ${MAX_AI_REQUEST_BYTES} bytes.`)\n }\n\n const reader = request.body.getReader()\n const chunks: Uint8Array[] = []\n let total = 0\n try {\n for (;;) {\n if (request.signal.aborted) throw request.signal.reason\n const { done, value } = await reader.read()\n if (done) break\n total += value.byteLength\n if (total > MAX_AI_REQUEST_BYTES) {\n await reader.cancel()\n throw new Error(`AI request body exceeded ${MAX_AI_REQUEST_BYTES} bytes.`)\n }\n chunks.push(value)\n }\n } finally {\n reader.releaseLock()\n }\n\n const body = new Uint8Array(total)\n let offset = 0\n for (const chunk of chunks) {\n body.set(chunk, offset)\n offset += chunk.byteLength\n }\n return body\n}\n\n/** Socket-pinned transport for tenant-configurable OpenAI-compatible endpoints. */\nexport const secureAiFetch: typeof globalThis.fetch = async (input, init) => {\n const request = new Request(input, init)\n const method = request.method.toUpperCase()\n if (!['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE'].includes(method)) {\n throw new Error(`AI request method ${method} is not supported.`)\n }\n const body = await readBoundedRequestBody(request)\n return secureFetch(request.url, {\n method: method as 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n headers: request.headers,\n body,\n timeoutMs: AI_REQUEST_TIMEOUT_MS,\n maxRequestBytes: MAX_AI_REQUEST_BYTES,\n maxResponseBytes: MAX_AI_RESPONSE_BYTES,\n maxRedirects: 2,\n signal: request.signal,\n })\n}\n\nexport function defaultModel(provider: AiProvider, tier: ModelTier): string {\n const spec = SPEC_BY_VALUE[provider] ?? SPEC_BY_VALUE.anthropic\n return tier === 'smart' ? spec.smart : spec.fast\n}\n\n/**\n * Whether the configured provider accepts IMAGE content in a tool result — the\n * capability that lets a tool hand rendered PDF pages back to the model for\n * vision reading. Currently Anthropic only; other providers' tool/function\n * results are text/JSON only, so exposing such a tool to them would break the\n * agent turn. Used to gate the assistant's `view_document_pages` tool.\n */\nexport function providerSupportsImageToolResults(config: AiConfig | null | undefined): boolean {\n if (!config) return false\n return SPEC_BY_VALUE[config.provider]?.visionToolResults ?? false\n}\n\nexport function isAiConfigured(config: AiConfig | null | undefined): config is AiConfig {\n if (!config || !config.apiKey) return false\n const spec = SPEC_BY_VALUE[config.provider]\n if (!spec) return false\n if (spec.requiresBaseUrl && !config.baseUrl?.trim()) return false\n return true\n}\n\n/** Resolve a language model from a tenant's config, or null when not configured. */\nexport function getModel(\n config: AiConfig | null | undefined,\n tier: ModelTier = 'fast',\n): LanguageModel | null {\n if (!isAiConfigured(config)) return null\n const spec = SPEC_BY_VALUE[config.provider]\n const modelId =\n (tier === 'smart' ? config.modelSmart : config.modelFast) || defaultModel(config.provider, tier)\n // `custom` has no default model — without an explicit one, AI stays disabled.\n if (!modelId) return null\n\n switch (spec.kind) {\n case 'anthropic':\n return createAnthropic({ apiKey: config.apiKey })(modelId)\n case 'openai':\n return createOpenAI({ apiKey: config.apiKey })(modelId)\n case 'google':\n return createGoogleGenerativeAI({ apiKey: config.apiKey })(modelId)\n case 'openai-compatible': {\n const baseURL = config.baseUrl?.trim() || spec.baseUrl\n if (!baseURL) return null\n return createOpenAICompatible({\n name: spec.value,\n apiKey: config.apiKey,\n baseURL,\n fetch: secureAiFetch,\n })(modelId)\n }\n }\n}\n\nexport class AIDisabledError extends Error {\n override readonly name = 'AIDisabledError'\n constructor() {\n super('No AI provider is available for this request.')\n }\n}\n\n/** Live test of a config — sends a tiny prompt and reports success/failure. */\nexport async function pingModel(\n config: AiConfig | null | undefined,\n): Promise<{ ok: boolean; message: string }> {\n const model = getModel(config, 'fast')\n if (!model) {\n return {\n ok: false,\n message: 'Not configured yet — check the provider, API key, model and base URL.',\n }\n }\n try {\n const { text } = await generateText({ model, prompt: 'Reply with the single word: ok' })\n return { ok: true, message: `Connected — the model replied “${text.trim().slice(0, 24)}”.` }\n } catch (e) {\n return { ok: false, message: e instanceof Error ? e.message.slice(0, 180) : 'Request failed.' }\n }\n}\n"]}
package/models.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // settings UI can offer dynamic dropdowns instead of free-text model ids.
3
3
  // Runs server-side only (the API key never leaves the server).
4
4
  import { providerSpec, validateAiBaseUrl } from './client.js';
5
- import { secureFetch } from '@braedonsaunders/appkit-sync/egress';
5
+ import { secureFetch } from '@braedonsaunders/appkit-egress-proxy/secure-fetch';
6
6
  function trimSlash(u) {
7
7
  let end = u.length;
8
8
  while (end > 0 && u.charCodeAt(end - 1) === 47)
package/models.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["// Model discovery — queries each provider's \"list models\" endpoint so the\n// settings UI can offer dynamic dropdowns instead of free-text model ids.\n// Runs server-side only (the API key never leaves the server).\n\nimport { providerSpec, validateAiBaseUrl, type AiConfig } from './client'\nimport { secureFetch } from '@braedonsaunders/appkit-sync/egress'\n\nexport type ModelListItem = { id: string; label?: string }\n\nfunction trimSlash(u: string): string {\n let end = u.length\n while (end > 0 && u.charCodeAt(end - 1) === 47) end -= 1\n return u.slice(0, end)\n}\n\nfunction dedupeSort(items: ModelListItem[]): ModelListItem[] {\n const map = new Map<string, ModelListItem>()\n for (const it of items) {\n if (it.id && !map.has(it.id)) map.set(it.id, it)\n }\n return [...map.values()].sort((a, b) => a.id.localeCompare(b.id))\n}\n\nasync function fetchJson(url: string, headers: Record<string, string>): Promise<unknown> {\n // Custom OpenAI-compatible endpoints are tenant-configured. Use the shared\n // HTTPS/public-DNS transport so redirects, DNS rebinding, private networks,\n // response size, and timeouts are enforced at the socket boundary.\n const res = await secureFetch(url, {\n headers,\n timeoutMs: 15_000,\n maxResponseBytes: 2 * 1024 * 1024,\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new Error(`${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 160)}` : ''}`)\n }\n return res.json()\n}\n\nfunction asArray(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value) ? (value as Record<string, unknown>[]) : []\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : value == null ? '' : String(value)\n}\n\n/** OpenAI returns every model (embeddings, tts, image…) — keep chat-capable text ones. */\nfunction isOpenAiChatModel(id: string): boolean {\n if (\n /embedding|whisper|tts|audio|dall-e|image|moderation|realtime|transcribe|search|similarity|edit|babbage|davinci/i.test(\n id,\n )\n ) {\n return false\n }\n return /^(gpt-|o1|o3|o4|chatgpt)/i.test(id)\n}\n\n/**\n * Fetch the available model ids for a provider config. Throws on HTTP / auth\n * errors so the caller can surface the provider's message.\n */\nexport async function listModels(config: AiConfig): Promise<ModelListItem[]> {\n const spec = providerSpec(config.provider)\n const key = config.apiKey\n\n switch (spec.kind) {\n case 'anthropic': {\n const json = await fetchJson('https://api.anthropic.com/v1/models?limit=1000', {\n 'x-api-key': key,\n 'anthropic-version': '2023-06-01',\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(\n data.map((m) => ({\n id: str(m.id),\n label: m.display_name ? str(m.display_name) : undefined,\n })),\n )\n }\n case 'openai': {\n const json = await fetchJson('https://api.openai.com/v1/models', {\n Authorization: `Bearer ${key}`,\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(data.map((m) => ({ id: str(m.id) })).filter((m) => isOpenAiChatModel(m.id)))\n }\n case 'google': {\n const json = await fetchJson(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}&pageSize=1000`,\n {},\n )\n const models = asArray((json as Record<string, unknown>)?.models)\n return dedupeSort(\n models\n .filter((m) => {\n const methods = m.supportedGenerationMethods\n return Array.isArray(methods) && methods.includes('generateContent')\n })\n .map((m) => ({\n id: str(m.name).replace(/^models\\//, ''),\n label: m.displayName ? str(m.displayName) : undefined,\n })),\n )\n }\n case 'openai-compatible': {\n const baseURL = (await validateAiBaseUrl(config.provider, config.baseUrl)) || spec.baseUrl\n if (!baseURL) throw new Error('A base URL is required to list models.')\n const json = await fetchJson(`${trimSlash(baseURL)}/models`, {\n Authorization: `Bearer ${key}`,\n })\n const root = json as Record<string, unknown>\n const data = asArray(root?.data).length ? asArray(root?.data) : asArray(root?.models)\n return dedupeSort(\n data.map((m) => {\n const id = str(m.id || m.name)\n const name = str(m.name)\n return { id, label: name && name !== id ? name : undefined }\n }),\n )\n }\n }\n}\n"]}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["// Model discovery — queries each provider's \"list models\" endpoint so the\n// settings UI can offer dynamic dropdowns instead of free-text model ids.\n// Runs server-side only (the API key never leaves the server).\n\nimport { providerSpec, validateAiBaseUrl, type AiConfig } from './client'\nimport { secureFetch } from '@braedonsaunders/appkit-egress-proxy/secure-fetch'\n\nexport type ModelListItem = { id: string; label?: string }\n\nfunction trimSlash(u: string): string {\n let end = u.length\n while (end > 0 && u.charCodeAt(end - 1) === 47) end -= 1\n return u.slice(0, end)\n}\n\nfunction dedupeSort(items: ModelListItem[]): ModelListItem[] {\n const map = new Map<string, ModelListItem>()\n for (const it of items) {\n if (it.id && !map.has(it.id)) map.set(it.id, it)\n }\n return [...map.values()].sort((a, b) => a.id.localeCompare(b.id))\n}\n\nasync function fetchJson(url: string, headers: Record<string, string>): Promise<unknown> {\n // Custom OpenAI-compatible endpoints are tenant-configured. Use the shared\n // HTTPS/public-DNS transport so redirects, DNS rebinding, private networks,\n // response size, and timeouts are enforced at the socket boundary.\n const res = await secureFetch(url, {\n headers,\n timeoutMs: 15_000,\n maxResponseBytes: 2 * 1024 * 1024,\n })\n if (!res.ok) {\n const body = await res.text().catch(() => '')\n throw new Error(`${res.status} ${res.statusText}${body ? ` — ${body.slice(0, 160)}` : ''}`)\n }\n return res.json()\n}\n\nfunction asArray(value: unknown): Record<string, unknown>[] {\n return Array.isArray(value) ? (value as Record<string, unknown>[]) : []\n}\n\nfunction str(value: unknown): string {\n return typeof value === 'string' ? value : value == null ? '' : String(value)\n}\n\n/** OpenAI returns every model (embeddings, tts, image…) — keep chat-capable text ones. */\nfunction isOpenAiChatModel(id: string): boolean {\n if (\n /embedding|whisper|tts|audio|dall-e|image|moderation|realtime|transcribe|search|similarity|edit|babbage|davinci/i.test(\n id,\n )\n ) {\n return false\n }\n return /^(gpt-|o1|o3|o4|chatgpt)/i.test(id)\n}\n\n/**\n * Fetch the available model ids for a provider config. Throws on HTTP / auth\n * errors so the caller can surface the provider's message.\n */\nexport async function listModels(config: AiConfig): Promise<ModelListItem[]> {\n const spec = providerSpec(config.provider)\n const key = config.apiKey\n\n switch (spec.kind) {\n case 'anthropic': {\n const json = await fetchJson('https://api.anthropic.com/v1/models?limit=1000', {\n 'x-api-key': key,\n 'anthropic-version': '2023-06-01',\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(\n data.map((m) => ({\n id: str(m.id),\n label: m.display_name ? str(m.display_name) : undefined,\n })),\n )\n }\n case 'openai': {\n const json = await fetchJson('https://api.openai.com/v1/models', {\n Authorization: `Bearer ${key}`,\n })\n const data = asArray((json as Record<string, unknown>)?.data)\n return dedupeSort(data.map((m) => ({ id: str(m.id) })).filter((m) => isOpenAiChatModel(m.id)))\n }\n case 'google': {\n const json = await fetchJson(\n `https://generativelanguage.googleapis.com/v1beta/models?key=${encodeURIComponent(key)}&pageSize=1000`,\n {},\n )\n const models = asArray((json as Record<string, unknown>)?.models)\n return dedupeSort(\n models\n .filter((m) => {\n const methods = m.supportedGenerationMethods\n return Array.isArray(methods) && methods.includes('generateContent')\n })\n .map((m) => ({\n id: str(m.name).replace(/^models\\//, ''),\n label: m.displayName ? str(m.displayName) : undefined,\n })),\n )\n }\n case 'openai-compatible': {\n const baseURL = (await validateAiBaseUrl(config.provider, config.baseUrl)) || spec.baseUrl\n if (!baseURL) throw new Error('A base URL is required to list models.')\n const json = await fetchJson(`${trimSlash(baseURL)}/models`, {\n Authorization: `Bearer ${key}`,\n })\n const root = json as Record<string, unknown>\n const data = asArray(root?.data).length ? asArray(root?.data) : asArray(root?.models)\n return dedupeSort(\n data.map((m) => {\n const id = str(m.id || m.name)\n const name = str(m.name)\n return { id, label: name && name !== id ? name : undefined }\n }),\n )\n }\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@braedonsaunders/appkit-ai",
3
- "version": "1.0.2",
3
+ "version": "1.1.0",
4
4
  "description": "App-agnostic bounded agent loop and streaming assistant interface, with provider policy supplied by the host app.",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "@ai-sdk/google": "^3.0.91",
25
25
  "@ai-sdk/openai": "^3.0.84",
26
26
  "@ai-sdk/openai-compatible": "^2.0.59",
27
- "@braedonsaunders/appkit-sync": "^1.1.0",
27
+ "@braedonsaunders/appkit-egress-proxy": "^0.1.1",
28
28
  "ai": "^6.0.224",
29
29
  "zod": "^4.4.3"
30
30
  },
package/react.d.ts CHANGED
@@ -4,6 +4,17 @@ export type AgentMessage = {
4
4
  role: 'user' | 'assistant' | 'system';
5
5
  parts: unknown[];
6
6
  };
7
+ export type AgentQueuedMessage = {
8
+ id: string;
9
+ text: string;
10
+ position: number;
11
+ status: 'queued' | 'dispatching' | 'retrying' | 'failed';
12
+ statusLabel?: string;
13
+ editable?: boolean;
14
+ removable?: boolean;
15
+ retryable?: boolean;
16
+ };
17
+ export type AgentDispatchState = 'idle' | 'running' | 'recovering';
7
18
  export type AgentPanelLabels = {
8
19
  title: string;
9
20
  welcomeTitle: string;
@@ -19,6 +30,16 @@ export type AgentPanelLabels = {
19
30
  working: string;
20
31
  step: string;
21
32
  steps: string;
33
+ queue: string;
34
+ queueTitle: string;
35
+ queuePosition: string;
36
+ queued: string;
37
+ dispatching: string;
38
+ retrying: string;
39
+ queueFailed: string;
40
+ editQueued: string;
41
+ removeQueued: string;
42
+ retryQueued: string;
22
43
  };
23
44
  export type AgentPanelProps = {
24
45
  enabled: boolean;
@@ -29,6 +50,15 @@ export type AgentPanelProps = {
29
50
  /** Replaces the stock empty-state card while preserving the panel header and composer. */
30
51
  emptyContent?: React.ReactNode;
31
52
  send?: (prompt: string, signal: AbortSignal) => Promise<Response>;
53
+ /** Durable dispatch state supplied by the application after reload or handoff. */
54
+ dispatchState?: AgentDispatchState;
55
+ /** Application-owned queue, already ordered by its durable dispatch position. */
56
+ queuedMessages?: readonly AgentQueuedMessage[];
57
+ /** Persists a turn behind the active or previously queued work. */
58
+ enqueue?: (prompt: string) => Promise<void>;
59
+ onEditQueuedMessage?: (message: AgentQueuedMessage) => void;
60
+ onRemoveQueuedMessage?: (message: AgentQueuedMessage) => void;
61
+ onRetryQueuedMessage?: (message: AgentQueuedMessage) => void;
32
62
  maxPromptCharacters?: number;
33
63
  toolLabels?: Record<string, string>;
34
64
  };
@@ -37,7 +67,14 @@ export type AgentPanelProps = {
37
67
  * owns persistence and the HTTP transport; appkit owns UI-message decoding,
38
68
  * cancellation, ordered part rendering, and tool cards.
39
69
  */
40
- export declare function AgentPanel({ enabled, initialMessages, suggestions, labels: labelOverrides, headerActions, emptyContent, send, maxPromptCharacters, toolLabels, }: AgentPanelProps): React.JSX.Element;
70
+ export declare function AgentPanel({ enabled, initialMessages, suggestions, labels: labelOverrides, headerActions, emptyContent, send, dispatchState, queuedMessages, enqueue, onEditQueuedMessage, onRemoveQueuedMessage, onRetryQueuedMessage, maxPromptCharacters, toolLabels, }: AgentPanelProps): React.JSX.Element;
71
+ export declare function AgentMessageQueue({ messages, labels: labelOverrides, onEdit, onRemove, onRetry, }: {
72
+ messages: readonly AgentQueuedMessage[];
73
+ labels?: Partial<AgentPanelLabels>;
74
+ onEdit?: (message: AgentQueuedMessage) => void;
75
+ onRemove?: (message: AgentQueuedMessage) => void;
76
+ onRetry?: (message: AgentQueuedMessage) => void;
77
+ }): React.JSX.Element;
41
78
  export declare function ChatMarkdown({ children }: {
42
79
  children: string;
43
80
  }): React.JSX.Element;
package/react.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAqB9B,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAA;IACrC,KAAK,EAAE,OAAO,EAAE,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAmBD,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAA;IAChB,eAAe,CAAC,EAAE,YAAY,EAAE,CAAA;IAChC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAA;IAClC,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC9B,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC,CAAA;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,eAAoB,EACpB,WAAgB,EAChB,MAAM,EAAE,cAAc,EACtB,aAAa,EACb,YAAY,EACZ,IAAI,EACJ,mBAA4B,EAC5B,UAAU,GACX,EAAE,eAAe,qBA0EjB;AAkFD,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,qBAE9D;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,UAAoB,EAAE,WAAsB,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,qBAK/O"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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"}
package/react.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from 'react';
4
4
  import { parseJsonEventStream, readUIMessageStream, uiMessageChunkSchema, } from 'ai';
5
- import { AlertCircle, CheckCircle2, ChevronRight, Database, Loader2, Send, Sparkles, Square, } from 'lucide-react';
5
+ import { AlertCircle, CheckCircle2, ChevronRight, Database, ListPlus, Loader2, Pencil, RotateCcw, Send, Sparkles, Square, Trash2, } from 'lucide-react';
6
6
  import Markdown from 'react-markdown';
7
7
  import remarkGfm from 'remark-gfm';
8
8
  import { Button, EmptyState, UiLink, cn } from '@braedonsaunders/appkit-ui';
@@ -21,17 +21,28 @@ const DEFAULT_LABELS = {
21
21
  working: 'Working',
22
22
  step: 'step',
23
23
  steps: 'steps',
24
+ queue: 'Add to queue',
25
+ queueTitle: 'Up next',
26
+ queuePosition: 'Position',
27
+ queued: 'Queued',
28
+ dispatching: 'Starting',
29
+ retrying: 'Retrying',
30
+ queueFailed: 'This queued message needs attention.',
31
+ editQueued: 'Edit queued message',
32
+ removeQueued: 'Remove queued message',
33
+ retryQueued: 'Retry queued message',
24
34
  };
25
35
  /**
26
36
  * The streaming thread/composer extracted from the sibling assistant. The app
27
37
  * owns persistence and the HTTP transport; appkit owns UI-message decoding,
28
38
  * cancellation, ordered part rendering, and tool cards.
29
39
  */
30
- export function AgentPanel({ enabled, initialMessages = [], suggestions = [], labels: labelOverrides, headerActions, emptyContent, send, maxPromptCharacters = 32_000, toolLabels, }) {
40
+ export function AgentPanel({ enabled, initialMessages = [], suggestions = [], labels: labelOverrides, headerActions, emptyContent, send, dispatchState = 'idle', queuedMessages = [], enqueue, onEditQueuedMessage, onRemoveQueuedMessage, onRetryQueuedMessage, maxPromptCharacters = 32_000, toolLabels, }) {
31
41
  const labels = { ...DEFAULT_LABELS, ...labelOverrides };
32
42
  const [messages, setMessages] = React.useState(initialMessages);
33
43
  const [input, setInput] = React.useState('');
34
44
  const [streaming, setStreaming] = React.useState(false);
45
+ const [enqueueing, setEnqueueing] = React.useState(false);
35
46
  const [error, setError] = React.useState(null);
36
47
  const abortRef = React.useRef(null);
37
48
  const messageViewportRef = React.useRef(null);
@@ -50,7 +61,27 @@ export function AgentPanel({ enabled, initialMessages = [], suggestions = [], la
50
61
  }, [scrollToBottom]);
51
62
  const submit = React.useCallback(async (raw) => {
52
63
  const prompt = raw.trim();
53
- if (!enabled || !send || !prompt || prompt.length > maxPromptCharacters || abortRef.current)
64
+ if (!enabled || !prompt || prompt.length > maxPromptCharacters)
65
+ return;
66
+ const shouldEnqueue = dispatchState !== 'idle' || abortRef.current !== null || queuedMessages.length > 0;
67
+ if (shouldEnqueue) {
68
+ if (!enqueue || enqueueing)
69
+ return;
70
+ setEnqueueing(true);
71
+ setError(null);
72
+ try {
73
+ await enqueue(prompt);
74
+ setInput('');
75
+ }
76
+ catch {
77
+ setError(labels.queueFailed);
78
+ }
79
+ finally {
80
+ setEnqueueing(false);
81
+ }
82
+ return;
83
+ }
84
+ if (!send)
54
85
  return;
55
86
  const controller = new AbortController();
56
87
  abortRef.current = controller;
@@ -89,11 +120,25 @@ export function AgentPanel({ enabled, initialMessages = [], suggestions = [], la
89
120
  if (abortRef.current === controller)
90
121
  abortRef.current = null;
91
122
  }
92
- }, [enabled, labels.failed, maxPromptCharacters, scrollToBottom, send]);
93
- return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col bg-bg-subtle", children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-2 border-b border-border bg-surface px-4", children: [_jsx(Sparkles, { size: 16, className: "text-primary" }), _jsx("span", { className: "text-sm font-medium text-fg", children: labels.title }), headerActions != null ? _jsx("div", { className: "ml-auto flex items-center gap-2", children: headerActions }) : null] }), _jsxs("div", { ref: messageViewportRef, className: "app-scroll min-h-0 flex-1 overflow-y-auto", children: [messages.length === 0 && emptyContent != null ? (_jsx("div", { className: "flex min-h-full flex-col", children: emptyContent })) : (_jsx("div", { className: "mx-auto w-full max-w-3xl px-4 py-6", children: messages.length === 0 ? _jsx(AgentWelcome, { enabled: enabled, title: enabled ? labels.welcomeTitle : labels.disabledTitle, description: enabled ? labels.welcomeDescription : labels.disabledDescription, suggestions: suggestions, onPick: (value) => void submit(value) }) : _jsx("div", { className: "space-y-6", children: messages.map((message) => message.role === 'system' ? null : _jsx(AgentMessageRow, { message: message, streaming: streaming, labels: labels, toolLabels: toolLabels }, message.id)) }) })), error ? _jsx("div", { role: "alert", className: "mx-auto mb-5 w-[calc(100%-2rem)] max-w-3xl rounded-lg border border-danger/25 bg-danger-subtle px-3 py-2 text-sm text-danger", children: error }) : null] }), enabled ? _jsx("div", { className: "shrink-0 border-t border-border bg-surface px-4 py-3", children: _jsx("div", { className: "mx-auto w-full max-w-3xl", children: _jsxs("div", { className: "flex items-end gap-2 rounded-2xl border border-border-strong bg-surface p-2 shadow-sm focus-within:border-primary focus-within:ring-2 focus-within:ring-ring/20", children: [_jsx("textarea", { value: input, onChange: (event) => setInput(event.target.value), onKeyDown: (event) => { if (event.key === 'Enter' && !event.shiftKey) {
94
- event.preventDefault();
95
- void submit(input);
96
- } }, maxLength: maxPromptCharacters, rows: 1, placeholder: labels.placeholder, className: "max-h-40 flex-1 resize-none overflow-y-auto border-0 bg-transparent px-2 py-1.5 text-base text-fg outline-none placeholder:text-fg-subtle sm:text-sm" }), streaming ? _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => abortRef.current?.abort(), "aria-label": labels.stop, children: _jsx(Square, { size: 16 }) }) : _jsx(Button, { type: "button", size: "icon", onClick: () => void submit(input), disabled: !input.trim() || !send, "aria-label": labels.send, children: _jsx(Send, { size: 16 }) })] }) }) }) : null] }));
123
+ }, [dispatchState, enabled, enqueue, enqueueing, labels.failed, labels.queueFailed, maxPromptCharacters, queuedMessages.length, scrollToBottom, send]);
124
+ const queueMode = dispatchState !== 'idle' || streaming || queuedMessages.length > 0;
125
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col bg-bg-subtle", children: [_jsxs("header", { className: "flex h-12 shrink-0 items-center gap-2 border-b border-border bg-surface px-4", children: [_jsx(Sparkles, { size: 16, className: "text-primary" }), _jsx("span", { className: "text-sm font-medium text-fg", children: labels.title }), headerActions != null ? _jsx("div", { className: "ml-auto flex items-center gap-2", children: headerActions }) : null] }), _jsxs("div", { ref: messageViewportRef, className: "app-scroll min-h-0 flex-1 overflow-y-auto", children: [messages.length === 0 && emptyContent != null ? (_jsx("div", { className: "flex min-h-full flex-col", children: emptyContent })) : (_jsx("div", { className: "mx-auto w-full max-w-3xl px-4 py-6", children: messages.length === 0 ? _jsx(AgentWelcome, { enabled: enabled, title: enabled ? labels.welcomeTitle : labels.disabledTitle, description: enabled ? labels.welcomeDescription : labels.disabledDescription, suggestions: suggestions, onPick: (value) => void submit(value) }) : _jsx("div", { className: "space-y-6", children: messages.map((message) => message.role === 'system' ? null : _jsx(AgentMessageRow, { message: message, streaming: streaming, labels: labels, toolLabels: toolLabels }, message.id)) }) })), error ? _jsx("div", { role: "alert", className: "mx-auto mb-5 w-[calc(100%-2rem)] max-w-3xl rounded-lg border border-danger/25 bg-danger-subtle px-3 py-2 text-sm text-danger", children: error }) : null] }), enabled ? _jsx("div", { className: "shrink-0 border-t border-border bg-surface px-4 py-3", children: _jsxs("div", { className: "mx-auto w-full max-w-3xl", children: [queuedMessages.length > 0 ? _jsx(AgentMessageQueue, { messages: queuedMessages, labels: labels, onEdit: onEditQueuedMessage, onRemove: onRemoveQueuedMessage, onRetry: onRetryQueuedMessage }) : null, _jsxs("div", { className: "flex items-end gap-2 rounded-2xl border border-border-strong bg-surface p-2 shadow-sm focus-within:border-primary focus-within:ring-2 focus-within:ring-ring/20", children: [_jsx("textarea", { value: input, onChange: (event) => setInput(event.target.value), onKeyDown: (event) => { if (event.key === 'Enter' && !event.shiftKey) {
126
+ event.preventDefault();
127
+ void submit(input);
128
+ } }, maxLength: maxPromptCharacters, rows: 1, placeholder: labels.placeholder, className: "max-h-40 flex-1 resize-none overflow-y-auto border-0 bg-transparent px-2 py-1.5 text-base text-fg outline-none placeholder:text-fg-subtle sm:text-sm" }), streaming ? _jsx(Button, { type: "button", variant: "outline", size: "icon", onClick: () => abortRef.current?.abort(), "aria-label": labels.stop, children: _jsx(Square, { size: 16 }) }) : null, _jsx(Button, { type: "button", size: "icon", onClick: () => void submit(input), disabled: !input.trim() || (queueMode ? !enqueue || enqueueing : !send), "aria-label": queueMode ? labels.queue : labels.send, children: enqueueing ? _jsx(Loader2, { size: 16, className: "animate-spin" }) : queueMode ? _jsx(ListPlus, { size: 16 }) : _jsx(Send, { size: 16 }) })] })] }) }) : null] }));
129
+ }
130
+ export function AgentMessageQueue({ messages, labels: labelOverrides, onEdit, onRemove, onRetry, }) {
131
+ const labels = { ...DEFAULT_LABELS, ...labelOverrides };
132
+ return (_jsxs("section", { className: "mb-3 rounded-xl border border-border bg-bg-subtle p-2", "aria-label": labels.queueTitle, children: [_jsx("div", { className: "px-2 pb-1 text-xs font-medium text-fg-muted", children: labels.queueTitle }), _jsx("ol", { className: "space-y-1", children: messages.map((message) => (_jsxs("li", { className: "flex min-w-0 items-center gap-2 rounded-lg bg-surface px-2 py-1.5 shadow-xs", children: [_jsx("span", { className: "flex size-6 shrink-0 items-center justify-center rounded-md bg-primary-subtle text-xs font-medium text-primary", "aria-label": `${labels.queuePosition} ${message.position}`, children: message.position }), _jsx("span", { className: "min-w-0 flex-1 truncate text-sm text-fg", children: message.text }), _jsx("span", { className: cn('shrink-0 text-xs', message.status === 'failed' ? 'text-danger' : 'text-fg-muted'), children: message.statusLabel ?? queueStatusLabel(message.status, labels) }), message.retryable && onRetry ? _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", onClick: () => onRetry(message), "aria-label": labels.retryQueued, children: _jsx(RotateCcw, { size: 14 }) }) : null, message.editable && onEdit ? _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", onClick: () => onEdit(message), "aria-label": labels.editQueued, children: _jsx(Pencil, { size: 14 }) }) : null, message.removable && onRemove ? _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "size-7", onClick: () => onRemove(message), "aria-label": labels.removeQueued, children: _jsx(Trash2, { size: 14 }) }) : null] }, message.id))) })] }));
133
+ }
134
+ function queueStatusLabel(status, labels) {
135
+ if (status === 'dispatching')
136
+ return labels.dispatching;
137
+ if (status === 'retrying')
138
+ return labels.retrying;
139
+ if (status === 'failed')
140
+ return labels.queueFailed;
141
+ return labels.queued;
97
142
  }
98
143
  function replaceLastAssistantParts(messages, parts) {
99
144
  const copy = messages.slice();
package/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {\n parseJsonEventStream,\n readUIMessageStream,\n uiMessageChunkSchema,\n type UIMessageChunk,\n} from 'ai'\nimport {\n AlertCircle,\n CheckCircle2,\n ChevronRight,\n Database,\n Loader2,\n Send,\n Sparkles,\n Square,\n} from 'lucide-react'\nimport Markdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { Button, EmptyState, UiLink, cn } from '@braedonsaunders/appkit-ui'\n\nexport type AgentMessage = {\n id: string\n role: 'user' | 'assistant' | 'system'\n parts: unknown[]\n}\n\nexport type AgentPanelLabels = {\n title: string\n welcomeTitle: string\n welcomeDescription: string\n disabledTitle: string\n disabledDescription: string\n placeholder: string\n send: string\n stop: string\n failed: string\n input: string\n result: string\n working: string\n step: string\n steps: string\n}\n\nconst DEFAULT_LABELS: AgentPanelLabels = {\n title: 'Assistant',\n welcomeTitle: 'How can I help?',\n welcomeDescription: 'Ask about your workspace or let the assistant use an approved tool.',\n disabledTitle: 'Connect an AI provider',\n disabledDescription: 'Connect an AI provider to enable agent conversations. No provider credentials are included in the demo.',\n placeholder: 'Ask the assistant…',\n send: 'Send',\n stop: 'Stop generating',\n failed: 'The assistant could not complete that turn. Please try again.',\n input: 'Input',\n result: 'Result',\n working: 'Working',\n step: 'step',\n steps: 'steps',\n}\n\nexport type AgentPanelProps = {\n enabled: boolean\n initialMessages?: AgentMessage[]\n suggestions?: string[]\n labels?: Partial<AgentPanelLabels>\n headerActions?: React.ReactNode\n /** Replaces the stock empty-state card while preserving the panel header and composer. */\n emptyContent?: React.ReactNode\n send?: (prompt: string, signal: AbortSignal) => Promise<Response>\n maxPromptCharacters?: number\n toolLabels?: Record<string, string>\n}\n\n/**\n * The streaming thread/composer extracted from the sibling assistant. The app\n * owns persistence and the HTTP transport; appkit owns UI-message decoding,\n * cancellation, ordered part rendering, and tool cards.\n */\nexport function AgentPanel({\n enabled,\n initialMessages = [],\n suggestions = [],\n labels: labelOverrides,\n headerActions,\n emptyContent,\n send,\n maxPromptCharacters = 32_000,\n toolLabels,\n}: AgentPanelProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides }\n const [messages, setMessages] = React.useState(initialMessages)\n const [input, setInput] = React.useState('')\n const [streaming, setStreaming] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const abortRef = React.useRef<AbortController | null>(null)\n const messageViewportRef = React.useRef<HTMLDivElement>(null)\n\n const scrollToBottom = React.useCallback(() => {\n window.requestAnimationFrame(() => {\n const viewport = messageViewportRef.current\n if (viewport) viewport.scrollTop = viewport.scrollHeight\n })\n }, [])\n\n // An existing conversation opens where work most recently happened. Keep\n // this scoped to the panel's own viewport: scrollIntoView also moves every\n // scrolling ancestor and can make an otherwise app-height page jump.\n React.useLayoutEffect(() => {\n scrollToBottom()\n }, [scrollToBottom])\n\n const submit = React.useCallback(async (raw: string) => {\n const prompt = raw.trim()\n if (!enabled || !send || !prompt || prompt.length > maxPromptCharacters || abortRef.current) return\n const controller = new AbortController()\n abortRef.current = controller\n const stamp = Date.now()\n setInput('')\n setError(null)\n setMessages((current) => [...current, { id: `user-${stamp}`, role: 'user', parts: [{ type: 'text', text: prompt }] }, { id: `assistant-${stamp}`, role: 'assistant', parts: [] }])\n setStreaming(true)\n scrollToBottom()\n let producedParts = false\n try {\n const response = await send(prompt, controller.signal)\n if (!response.ok || !response.body) throw new Error('agent request failed')\n const chunks = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema }).pipeThrough(new TransformStream<{ success: boolean; value?: UIMessageChunk }, UIMessageChunk>({ transform(part, stream) { if (part.success && part.value) stream.enqueue(part.value) } }))\n let lastParts: unknown[] = []\n for await (const message of readUIMessageStream({ stream: chunks })) {\n lastParts = message.parts as unknown[]\n producedParts = lastParts.length > 0\n setMessages((current) => replaceLastAssistantParts(current, lastParts))\n scrollToBottom()\n }\n if (lastParts.length === 0 && !controller.signal.aborted) setError(labels.failed)\n } catch (reason) {\n if ((reason as Error).name !== 'AbortError') setError(labels.failed)\n } finally {\n if (!producedParts) {\n setMessages((current) => current.filter((message) => message.id !== `assistant-${stamp}`))\n }\n setStreaming(false)\n if (abortRef.current === controller) abortRef.current = null\n }\n }, [enabled, labels.failed, maxPromptCharacters, scrollToBottom, send])\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col bg-bg-subtle\">\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border bg-surface px-4\"><Sparkles size={16} className=\"text-primary\" /><span className=\"text-sm font-medium text-fg\">{labels.title}</span>{headerActions != null ? <div className=\"ml-auto flex items-center gap-2\">{headerActions}</div> : null}</header>\n <div ref={messageViewportRef} className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">\n {messages.length === 0 && emptyContent != null ? (\n <div className=\"flex min-h-full flex-col\">{emptyContent}</div>\n ) : (\n <div className=\"mx-auto w-full max-w-3xl px-4 py-6\">\n {messages.length === 0 ? <AgentWelcome enabled={enabled} title={enabled ? labels.welcomeTitle : labels.disabledTitle} description={enabled ? labels.welcomeDescription : labels.disabledDescription} suggestions={suggestions} onPick={(value) => void submit(value)} /> : <div className=\"space-y-6\">{messages.map((message) => message.role === 'system' ? null : <AgentMessageRow key={message.id} message={message} streaming={streaming} labels={labels} toolLabels={toolLabels} />)}</div>}\n </div>\n )}\n {error ? <div role=\"alert\" className=\"mx-auto mb-5 w-[calc(100%-2rem)] max-w-3xl rounded-lg border border-danger/25 bg-danger-subtle px-3 py-2 text-sm text-danger\">{error}</div> : null}\n </div>\n {enabled ? <div className=\"shrink-0 border-t border-border bg-surface px-4 py-3\"><div className=\"mx-auto w-full max-w-3xl\"><div className=\"flex items-end gap-2 rounded-2xl border border-border-strong bg-surface p-2 shadow-sm focus-within:border-primary focus-within:ring-2 focus-within:ring-ring/20\"><textarea value={input} onChange={(event) => setInput(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); void submit(input) } }} maxLength={maxPromptCharacters} rows={1} placeholder={labels.placeholder} className=\"max-h-40 flex-1 resize-none overflow-y-auto border-0 bg-transparent px-2 py-1.5 text-base text-fg outline-none placeholder:text-fg-subtle sm:text-sm\" />{streaming ? <Button type=\"button\" variant=\"outline\" size=\"icon\" onClick={() => abortRef.current?.abort()} aria-label={labels.stop}><Square size={16} /></Button> : <Button type=\"button\" size=\"icon\" onClick={() => void submit(input)} disabled={!input.trim() || !send} aria-label={labels.send}><Send size={16} /></Button>}</div></div></div> : null}\n </div>\n )\n}\n\nfunction replaceLastAssistantParts(messages: AgentMessage[], parts: unknown[]): AgentMessage[] {\n const copy = messages.slice()\n for (let index = copy.length - 1; index >= 0; index -= 1) {\n if (copy[index]?.role === 'assistant') { copy[index] = { ...copy[index]!, parts }; break }\n }\n return copy\n}\n\nfunction AgentWelcome({ enabled, title, description, suggestions, onPick }: { enabled: boolean; title: string; description: string; suggestions: string[]; onPick: (value: string) => void }) {\n return <div className=\"pt-10\"><EmptyState icon={<Sparkles />} title={title} description={description} />{enabled && suggestions.length ? <div className=\"mx-auto mt-6 grid max-w-2xl gap-2 sm:grid-cols-2\">{suggestions.map((suggestion) => <button key={suggestion} type=\"button\" onClick={() => onPick(suggestion)} className=\"rounded-xl border border-border bg-surface px-4 py-3 text-left text-sm text-fg-muted shadow-sm transition-colors hover:border-primary/40 hover:bg-primary-subtle hover:text-fg\">{suggestion}</button>)}</div> : null}</div>\n}\n\nfunction AgentMessageRow({ message, streaming, labels, toolLabels }: { message: AgentMessage; streaming: boolean; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n if (message.role === 'user') {\n const text = (message.parts.find((part) => (part as { type?: string }).type === 'text') as { text?: string } | undefined)?.text\n return <div className=\"flex justify-end\"><div className=\"max-w-[85%] rounded-2xl rounded-br-md bg-primary px-4 py-2 text-sm whitespace-pre-wrap text-primary-fg\">{text}</div></div>\n }\n return <div className=\"flex gap-3\"><span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-primary text-primary-fg shadow-sm\"><Sparkles size={16} /></span><div className=\"min-w-0 flex-1 pt-0.5\">{message.parts.length === 0 && streaming ? <div className=\"flex items-center gap-1 py-1.5\">{[0,1,2].map((index) => <span key={index} className=\"size-1.5 animate-bounce rounded-full bg-fg-subtle\" style={{ animationDelay: `${index * 0.15}s` }} />)}</div> : <AgentMessageParts parts={message.parts} labels={labels} toolLabels={toolLabels} />}</div></div>\n}\n\nfunction AgentMessageParts({ parts, labels, toolLabels }: { parts: unknown[]; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n const rendered: React.ReactNode[] = []\n let tools: AgentToolPart[] = []\n let toolGroupStart = 0\n const flushTools = () => {\n if (tools.length === 0) return\n rendered.push(<AgentToolActivity key={`tools-${toolGroupStart}`} parts={tools} labels={labels} toolLabels={toolLabels} />)\n tools = []\n }\n ;(parts as { type: string; [key: string]: unknown }[]).forEach((part, index) => {\n if (isAgentToolPart(part)) {\n if (tools.length === 0) toolGroupStart = index\n tools.push(part)\n return\n }\n if (part.type === 'step-start' || part.type === 'reasoning') return\n flushTools()\n if (part.type === 'text' && typeof part.text === 'string' && part.text.trim()) {\n rendered.push(<ChatMarkdown key={`text-${index}`}>{part.text}</ChatMarkdown>)\n }\n })\n flushTools()\n return <div className=\"space-y-2.5\">{rendered}</div>\n}\n\ntype AgentToolPart = { type: string; toolName?: unknown; state?: unknown; input?: unknown; output?: unknown }\n\nfunction isAgentToolPart(part: { type: string }): part is AgentToolPart {\n return part.type === 'dynamic-tool' || part.type.startsWith('tool-')\n}\n\nfunction agentToolName(part: AgentToolPart): string {\n return part.type === 'dynamic-tool' ? String(part.toolName ?? 'tool') : part.type.slice(5)\n}\n\nfunction agentToolRunning(part: AgentToolPart): boolean {\n const state = String(part.state ?? 'output-available')\n return state === 'input-streaming' || state === 'input-available'\n}\n\nfunction agentToolErrored(part: AgentToolPart): boolean {\n return String(part.state) === 'output-error' || (part.output as { ok?: boolean } | undefined)?.ok === false\n}\n\n/**\n * A multi-step run stays quiet in the transcript: the newest action and total\n * step count occupy one line, while the complete inputs/results remain one\n * click away. Applications keep their full durable audit trail separately.\n */\nfunction AgentToolActivity({ parts, labels, toolLabels }: { parts: AgentToolPart[]; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n const [open, setOpen] = React.useState(false)\n const latest = parts.at(-1)!\n const latestName = agentToolName(latest)\n const running = parts.some(agentToolRunning)\n const errored = parts.some(agentToolErrored)\n const count = parts.length\n const status = running ? labels.working : `${count} ${count === 1 ? labels.step : labels.steps}`\n return <div className=\"text-sm\"><button type=\"button\" onClick={() => setOpen((value) => !value)} aria-expanded={open} className=\"flex h-7 max-w-full items-center gap-1.5 rounded-md px-1.5 text-left text-xs text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg\"><span className={cn('size-1.5 shrink-0 rounded-full', running ? 'animate-pulse bg-primary' : errored ? 'bg-danger' : 'bg-success')} /><span className=\"shrink-0 font-medium\">{status}</span><span aria-hidden className=\"text-fg-subtle\">·</span><span className=\"min-w-0 truncate\">{toolLabels?.[latestName] ?? latestName.replaceAll('_', ' ')}</span><ChevronRight size={12} className={cn('shrink-0 text-fg-subtle transition-transform', open && 'rotate-90')} /></button>{open ? <div className=\"mt-1.5 space-y-1.5 border-l border-border pl-2\">{parts.map((part, index) => { const name = agentToolName(part); return <AgentToolCard key={`${name}-${index}`} name={name} label={toolLabels?.[name]} state={String(part.state ?? 'output-available')} input={part.input} output={part.output} inputLabel={labels.input} resultLabel={labels.result} /> })}</div> : null}</div>\n}\n\nexport function ChatMarkdown({ children }: { children: string }) {\n return <div className=\"space-y-2 text-sm leading-relaxed text-fg\"><Markdown remarkPlugins={[remarkGfm]} components={{ p: ({ children: content }) => <p className=\"whitespace-pre-wrap\">{content}</p>, h1: ({ children: content }) => <h1 className=\"text-lg font-semibold\">{content}</h1>, h2: ({ children: content }) => <h2 className=\"text-base font-semibold\">{content}</h2>, ul: ({ children: content }) => <ul className=\"list-disc space-y-1 pl-5\">{content}</ul>, ol: ({ children: content }) => <ol className=\"list-decimal space-y-1 pl-5\">{content}</ol>, code: ({ children: content }) => <code className=\"rounded bg-bg-subtle px-1 py-0.5 font-mono text-[0.85em] text-primary\">{content}</code>, pre: ({ children: content }) => <pre className=\"overflow-auto rounded-lg bg-overlay p-3 text-sm whitespace-pre-wrap text-white\">{content}</pre>, table: ({ children: content }) => <div className=\"overflow-x-auto\"><table className=\"w-full border-collapse text-sm\">{content}</table></div>, th: ({ children: content }) => <th className=\"border-b border-border px-2 py-1 text-left\">{content}</th>, td: ({ children: content }) => <td className=\"border-b border-border-subtle px-2 py-1\">{content}</td>, a: ({ href, children: content }) => href?.startsWith('/') ? <UiLink href={href} className=\"font-medium text-primary underline-offset-2 hover:underline\">{content}</UiLink> : <a href={href} target=\"_blank\" rel=\"noreferrer\" className=\"font-medium text-primary underline-offset-2 hover:underline\">{content}</a> }}>{children}</Markdown></div>\n}\n\nexport function AgentToolCard({ name, label, state, input, output, inputLabel = 'Input', resultLabel = 'Result' }: { name: string; label?: string; state: string; input?: unknown; output?: unknown; inputLabel?: string; resultLabel?: string }) {\n const [open, setOpen] = React.useState(false)\n const running = state === 'input-streaming' || state === 'input-available'\n const errored = state === 'output-error' || (output as { ok?: boolean } | undefined)?.ok === false\n return <div className=\"overflow-hidden rounded-lg border border-border bg-bg-subtle text-sm\"><button type=\"button\" onClick={() => setOpen((value) => !value)} className=\"flex w-full items-center gap-2.5 px-3 py-2 text-left transition-colors hover:bg-surface-hover\"><span className={cn('flex size-6 shrink-0 items-center justify-center rounded-md', errored ? 'bg-danger-subtle text-danger' : 'bg-primary-subtle text-primary')}><Database size={14} /></span><span className=\"min-w-0 flex-1 truncate font-medium text-fg\">{label ?? name.replaceAll('_', ' ')}</span>{running ? <Loader2 size={14} className=\"animate-spin text-fg-subtle\" /> : errored ? <AlertCircle size={14} className=\"text-danger\" /> : <CheckCircle2 size={14} className=\"text-success\" />}<ChevronRight size={14} className={cn('text-fg-subtle transition-transform', open && 'rotate-90')} /></button>{open ? <div className=\"space-y-2 border-t border-border px-3 py-2\">{input !== undefined ? <AgentToolDetail label={inputLabel} value={input} /> : null}{output !== undefined ? <AgentToolDetail label={resultLabel} value={output} /> : null}</div> : null}</div>\n}\n\nfunction AgentToolDetail({ label, value }: { label: string; value: unknown }) {\n let text: string\n try { text = JSON.stringify(value, null, 2) } catch { text = String(value) }\n return <div><div className=\"mb-1 text-[11px] font-semibold tracking-wide text-fg-subtle uppercase\">{label}</div><pre className=\"max-h-60 overflow-auto rounded-md bg-surface p-2 text-xs leading-relaxed text-fg ring-1 ring-border\">{text}</pre></div>\n}\n"]}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../src/react.tsx"],"names":[],"mappings":"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","sourcesContent":["'use client'\n\nimport * as React from 'react'\nimport {\n parseJsonEventStream,\n readUIMessageStream,\n uiMessageChunkSchema,\n type UIMessageChunk,\n} from 'ai'\nimport {\n AlertCircle,\n CheckCircle2,\n ChevronRight,\n Database,\n ListPlus,\n Loader2,\n Pencil,\n RotateCcw,\n Send,\n Sparkles,\n Square,\n Trash2,\n} from 'lucide-react'\nimport Markdown from 'react-markdown'\nimport remarkGfm from 'remark-gfm'\nimport { Button, EmptyState, UiLink, cn } from '@braedonsaunders/appkit-ui'\n\nexport type AgentMessage = {\n id: string\n role: 'user' | 'assistant' | 'system'\n parts: unknown[]\n}\n\nexport type AgentQueuedMessage = {\n id: string\n text: string\n position: number\n status: 'queued' | 'dispatching' | 'retrying' | 'failed'\n statusLabel?: string\n editable?: boolean\n removable?: boolean\n retryable?: boolean\n}\n\nexport type AgentDispatchState = 'idle' | 'running' | 'recovering'\n\nexport type AgentPanelLabels = {\n title: string\n welcomeTitle: string\n welcomeDescription: string\n disabledTitle: string\n disabledDescription: string\n placeholder: string\n send: string\n stop: string\n failed: string\n input: string\n result: string\n working: string\n step: string\n steps: string\n queue: string\n queueTitle: string\n queuePosition: string\n queued: string\n dispatching: string\n retrying: string\n queueFailed: string\n editQueued: string\n removeQueued: string\n retryQueued: string\n}\n\nconst DEFAULT_LABELS: AgentPanelLabels = {\n title: 'Assistant',\n welcomeTitle: 'How can I help?',\n welcomeDescription: 'Ask about your workspace or let the assistant use an approved tool.',\n disabledTitle: 'Connect an AI provider',\n disabledDescription: 'Connect an AI provider to enable agent conversations. No provider credentials are included in the demo.',\n placeholder: 'Ask the assistant…',\n send: 'Send',\n stop: 'Stop generating',\n failed: 'The assistant could not complete that turn. Please try again.',\n input: 'Input',\n result: 'Result',\n working: 'Working',\n step: 'step',\n steps: 'steps',\n queue: 'Add to queue',\n queueTitle: 'Up next',\n queuePosition: 'Position',\n queued: 'Queued',\n dispatching: 'Starting',\n retrying: 'Retrying',\n queueFailed: 'This queued message needs attention.',\n editQueued: 'Edit queued message',\n removeQueued: 'Remove queued message',\n retryQueued: 'Retry queued message',\n}\n\nexport type AgentPanelProps = {\n enabled: boolean\n initialMessages?: AgentMessage[]\n suggestions?: string[]\n labels?: Partial<AgentPanelLabels>\n headerActions?: React.ReactNode\n /** Replaces the stock empty-state card while preserving the panel header and composer. */\n emptyContent?: React.ReactNode\n send?: (prompt: string, signal: AbortSignal) => Promise<Response>\n /** Durable dispatch state supplied by the application after reload or handoff. */\n dispatchState?: AgentDispatchState\n /** Application-owned queue, already ordered by its durable dispatch position. */\n queuedMessages?: readonly AgentQueuedMessage[]\n /** Persists a turn behind the active or previously queued work. */\n enqueue?: (prompt: string) => Promise<void>\n onEditQueuedMessage?: (message: AgentQueuedMessage) => void\n onRemoveQueuedMessage?: (message: AgentQueuedMessage) => void\n onRetryQueuedMessage?: (message: AgentQueuedMessage) => void\n maxPromptCharacters?: number\n toolLabels?: Record<string, string>\n}\n\n/**\n * The streaming thread/composer extracted from the sibling assistant. The app\n * owns persistence and the HTTP transport; appkit owns UI-message decoding,\n * cancellation, ordered part rendering, and tool cards.\n */\nexport function AgentPanel({\n enabled,\n initialMessages = [],\n suggestions = [],\n labels: labelOverrides,\n headerActions,\n emptyContent,\n send,\n dispatchState = 'idle',\n queuedMessages = [],\n enqueue,\n onEditQueuedMessage,\n onRemoveQueuedMessage,\n onRetryQueuedMessage,\n maxPromptCharacters = 32_000,\n toolLabels,\n}: AgentPanelProps) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides }\n const [messages, setMessages] = React.useState(initialMessages)\n const [input, setInput] = React.useState('')\n const [streaming, setStreaming] = React.useState(false)\n const [enqueueing, setEnqueueing] = React.useState(false)\n const [error, setError] = React.useState<string | null>(null)\n const abortRef = React.useRef<AbortController | null>(null)\n const messageViewportRef = React.useRef<HTMLDivElement>(null)\n\n const scrollToBottom = React.useCallback(() => {\n window.requestAnimationFrame(() => {\n const viewport = messageViewportRef.current\n if (viewport) viewport.scrollTop = viewport.scrollHeight\n })\n }, [])\n\n // An existing conversation opens where work most recently happened. Keep\n // this scoped to the panel's own viewport: scrollIntoView also moves every\n // scrolling ancestor and can make an otherwise app-height page jump.\n React.useLayoutEffect(() => {\n scrollToBottom()\n }, [scrollToBottom])\n\n const submit = React.useCallback(async (raw: string) => {\n const prompt = raw.trim()\n if (!enabled || !prompt || prompt.length > maxPromptCharacters) return\n const shouldEnqueue = dispatchState !== 'idle' || abortRef.current !== null || queuedMessages.length > 0\n if (shouldEnqueue) {\n if (!enqueue || enqueueing) return\n setEnqueueing(true)\n setError(null)\n try {\n await enqueue(prompt)\n setInput('')\n } catch {\n setError(labels.queueFailed)\n } finally {\n setEnqueueing(false)\n }\n return\n }\n if (!send) return\n const controller = new AbortController()\n abortRef.current = controller\n const stamp = Date.now()\n setInput('')\n setError(null)\n setMessages((current) => [...current, { id: `user-${stamp}`, role: 'user', parts: [{ type: 'text', text: prompt }] }, { id: `assistant-${stamp}`, role: 'assistant', parts: [] }])\n setStreaming(true)\n scrollToBottom()\n let producedParts = false\n try {\n const response = await send(prompt, controller.signal)\n if (!response.ok || !response.body) throw new Error('agent request failed')\n const chunks = parseJsonEventStream({ stream: response.body, schema: uiMessageChunkSchema }).pipeThrough(new TransformStream<{ success: boolean; value?: UIMessageChunk }, UIMessageChunk>({ transform(part, stream) { if (part.success && part.value) stream.enqueue(part.value) } }))\n let lastParts: unknown[] = []\n for await (const message of readUIMessageStream({ stream: chunks })) {\n lastParts = message.parts as unknown[]\n producedParts = lastParts.length > 0\n setMessages((current) => replaceLastAssistantParts(current, lastParts))\n scrollToBottom()\n }\n if (lastParts.length === 0 && !controller.signal.aborted) setError(labels.failed)\n } catch (reason) {\n if ((reason as Error).name !== 'AbortError') setError(labels.failed)\n } finally {\n if (!producedParts) {\n setMessages((current) => current.filter((message) => message.id !== `assistant-${stamp}`))\n }\n setStreaming(false)\n if (abortRef.current === controller) abortRef.current = null\n }\n }, [dispatchState, enabled, enqueue, enqueueing, labels.failed, labels.queueFailed, maxPromptCharacters, queuedMessages.length, scrollToBottom, send])\n\n const queueMode = dispatchState !== 'idle' || streaming || queuedMessages.length > 0\n\n return (\n <div className=\"flex min-h-0 flex-1 flex-col bg-bg-subtle\">\n <header className=\"flex h-12 shrink-0 items-center gap-2 border-b border-border bg-surface px-4\"><Sparkles size={16} className=\"text-primary\" /><span className=\"text-sm font-medium text-fg\">{labels.title}</span>{headerActions != null ? <div className=\"ml-auto flex items-center gap-2\">{headerActions}</div> : null}</header>\n <div ref={messageViewportRef} className=\"app-scroll min-h-0 flex-1 overflow-y-auto\">\n {messages.length === 0 && emptyContent != null ? (\n <div className=\"flex min-h-full flex-col\">{emptyContent}</div>\n ) : (\n <div className=\"mx-auto w-full max-w-3xl px-4 py-6\">\n {messages.length === 0 ? <AgentWelcome enabled={enabled} title={enabled ? labels.welcomeTitle : labels.disabledTitle} description={enabled ? labels.welcomeDescription : labels.disabledDescription} suggestions={suggestions} onPick={(value) => void submit(value)} /> : <div className=\"space-y-6\">{messages.map((message) => message.role === 'system' ? null : <AgentMessageRow key={message.id} message={message} streaming={streaming} labels={labels} toolLabels={toolLabels} />)}</div>}\n </div>\n )}\n {error ? <div role=\"alert\" className=\"mx-auto mb-5 w-[calc(100%-2rem)] max-w-3xl rounded-lg border border-danger/25 bg-danger-subtle px-3 py-2 text-sm text-danger\">{error}</div> : null}\n </div>\n {enabled ? <div className=\"shrink-0 border-t border-border bg-surface px-4 py-3\"><div className=\"mx-auto w-full max-w-3xl\">{queuedMessages.length > 0 ? <AgentMessageQueue messages={queuedMessages} labels={labels} onEdit={onEditQueuedMessage} onRemove={onRemoveQueuedMessage} onRetry={onRetryQueuedMessage} /> : null}<div className=\"flex items-end gap-2 rounded-2xl border border-border-strong bg-surface p-2 shadow-sm focus-within:border-primary focus-within:ring-2 focus-within:ring-ring/20\"><textarea value={input} onChange={(event) => setInput(event.target.value)} onKeyDown={(event) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); void submit(input) } }} maxLength={maxPromptCharacters} rows={1} placeholder={labels.placeholder} className=\"max-h-40 flex-1 resize-none overflow-y-auto border-0 bg-transparent px-2 py-1.5 text-base text-fg outline-none placeholder:text-fg-subtle sm:text-sm\" />{streaming ? <Button type=\"button\" variant=\"outline\" size=\"icon\" onClick={() => abortRef.current?.abort()} aria-label={labels.stop}><Square size={16} /></Button> : null}<Button type=\"button\" size=\"icon\" onClick={() => void submit(input)} disabled={!input.trim() || (queueMode ? !enqueue || enqueueing : !send)} aria-label={queueMode ? labels.queue : labels.send}>{enqueueing ? <Loader2 size={16} className=\"animate-spin\" /> : queueMode ? <ListPlus size={16} /> : <Send size={16} />}</Button></div></div></div> : null}\n </div>\n )\n}\n\nexport function AgentMessageQueue({\n messages,\n labels: labelOverrides,\n onEdit,\n onRemove,\n onRetry,\n}: {\n messages: readonly AgentQueuedMessage[]\n labels?: Partial<AgentPanelLabels>\n onEdit?: (message: AgentQueuedMessage) => void\n onRemove?: (message: AgentQueuedMessage) => void\n onRetry?: (message: AgentQueuedMessage) => void\n}) {\n const labels = { ...DEFAULT_LABELS, ...labelOverrides }\n return (\n <section className=\"mb-3 rounded-xl border border-border bg-bg-subtle p-2\" aria-label={labels.queueTitle}>\n <div className=\"px-2 pb-1 text-xs font-medium text-fg-muted\">{labels.queueTitle}</div>\n <ol className=\"space-y-1\">\n {messages.map((message) => (\n <li key={message.id} className=\"flex min-w-0 items-center gap-2 rounded-lg bg-surface px-2 py-1.5 shadow-xs\">\n <span className=\"flex size-6 shrink-0 items-center justify-center rounded-md bg-primary-subtle text-xs font-medium text-primary\" aria-label={`${labels.queuePosition} ${message.position}`}>{message.position}</span>\n <span className=\"min-w-0 flex-1 truncate text-sm text-fg\">{message.text}</span>\n <span className={cn('shrink-0 text-xs', message.status === 'failed' ? 'text-danger' : 'text-fg-muted')}>{message.statusLabel ?? queueStatusLabel(message.status, labels)}</span>\n {message.retryable && onRetry ? <Button type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={() => onRetry(message)} aria-label={labels.retryQueued}><RotateCcw size={14} /></Button> : null}\n {message.editable && onEdit ? <Button type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={() => onEdit(message)} aria-label={labels.editQueued}><Pencil size={14} /></Button> : null}\n {message.removable && onRemove ? <Button type=\"button\" variant=\"ghost\" size=\"icon\" className=\"size-7\" onClick={() => onRemove(message)} aria-label={labels.removeQueued}><Trash2 size={14} /></Button> : null}\n </li>\n ))}\n </ol>\n </section>\n )\n}\n\nfunction queueStatusLabel(status: AgentQueuedMessage['status'], labels: AgentPanelLabels): string {\n if (status === 'dispatching') return labels.dispatching\n if (status === 'retrying') return labels.retrying\n if (status === 'failed') return labels.queueFailed\n return labels.queued\n}\n\nfunction replaceLastAssistantParts(messages: AgentMessage[], parts: unknown[]): AgentMessage[] {\n const copy = messages.slice()\n for (let index = copy.length - 1; index >= 0; index -= 1) {\n if (copy[index]?.role === 'assistant') { copy[index] = { ...copy[index]!, parts }; break }\n }\n return copy\n}\n\nfunction AgentWelcome({ enabled, title, description, suggestions, onPick }: { enabled: boolean; title: string; description: string; suggestions: string[]; onPick: (value: string) => void }) {\n return <div className=\"pt-10\"><EmptyState icon={<Sparkles />} title={title} description={description} />{enabled && suggestions.length ? <div className=\"mx-auto mt-6 grid max-w-2xl gap-2 sm:grid-cols-2\">{suggestions.map((suggestion) => <button key={suggestion} type=\"button\" onClick={() => onPick(suggestion)} className=\"rounded-xl border border-border bg-surface px-4 py-3 text-left text-sm text-fg-muted shadow-sm transition-colors hover:border-primary/40 hover:bg-primary-subtle hover:text-fg\">{suggestion}</button>)}</div> : null}</div>\n}\n\nfunction AgentMessageRow({ message, streaming, labels, toolLabels }: { message: AgentMessage; streaming: boolean; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n if (message.role === 'user') {\n const text = (message.parts.find((part) => (part as { type?: string }).type === 'text') as { text?: string } | undefined)?.text\n return <div className=\"flex justify-end\"><div className=\"max-w-[85%] rounded-2xl rounded-br-md bg-primary px-4 py-2 text-sm whitespace-pre-wrap text-primary-fg\">{text}</div></div>\n }\n return <div className=\"flex gap-3\"><span className=\"mt-0.5 flex size-7 shrink-0 items-center justify-center rounded-full bg-primary text-primary-fg shadow-sm\"><Sparkles size={16} /></span><div className=\"min-w-0 flex-1 pt-0.5\">{message.parts.length === 0 && streaming ? <div className=\"flex items-center gap-1 py-1.5\">{[0,1,2].map((index) => <span key={index} className=\"size-1.5 animate-bounce rounded-full bg-fg-subtle\" style={{ animationDelay: `${index * 0.15}s` }} />)}</div> : <AgentMessageParts parts={message.parts} labels={labels} toolLabels={toolLabels} />}</div></div>\n}\n\nfunction AgentMessageParts({ parts, labels, toolLabels }: { parts: unknown[]; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n const rendered: React.ReactNode[] = []\n let tools: AgentToolPart[] = []\n let toolGroupStart = 0\n const flushTools = () => {\n if (tools.length === 0) return\n rendered.push(<AgentToolActivity key={`tools-${toolGroupStart}`} parts={tools} labels={labels} toolLabels={toolLabels} />)\n tools = []\n }\n ;(parts as { type: string; [key: string]: unknown }[]).forEach((part, index) => {\n if (isAgentToolPart(part)) {\n if (tools.length === 0) toolGroupStart = index\n tools.push(part)\n return\n }\n if (part.type === 'step-start' || part.type === 'reasoning') return\n flushTools()\n if (part.type === 'text' && typeof part.text === 'string' && part.text.trim()) {\n rendered.push(<ChatMarkdown key={`text-${index}`}>{part.text}</ChatMarkdown>)\n }\n })\n flushTools()\n return <div className=\"space-y-2.5\">{rendered}</div>\n}\n\ntype AgentToolPart = { type: string; toolName?: unknown; state?: unknown; input?: unknown; output?: unknown }\n\nfunction isAgentToolPart(part: { type: string }): part is AgentToolPart {\n return part.type === 'dynamic-tool' || part.type.startsWith('tool-')\n}\n\nfunction agentToolName(part: AgentToolPart): string {\n return part.type === 'dynamic-tool' ? String(part.toolName ?? 'tool') : part.type.slice(5)\n}\n\nfunction agentToolRunning(part: AgentToolPart): boolean {\n const state = String(part.state ?? 'output-available')\n return state === 'input-streaming' || state === 'input-available'\n}\n\nfunction agentToolErrored(part: AgentToolPart): boolean {\n return String(part.state) === 'output-error' || (part.output as { ok?: boolean } | undefined)?.ok === false\n}\n\n/**\n * A multi-step run stays quiet in the transcript: the newest action and total\n * step count occupy one line, while the complete inputs/results remain one\n * click away. Applications keep their full durable audit trail separately.\n */\nfunction AgentToolActivity({ parts, labels, toolLabels }: { parts: AgentToolPart[]; labels: AgentPanelLabels; toolLabels?: Record<string, string> }) {\n const [open, setOpen] = React.useState(false)\n const latest = parts.at(-1)!\n const latestName = agentToolName(latest)\n const running = parts.some(agentToolRunning)\n const errored = parts.some(agentToolErrored)\n const count = parts.length\n const status = running ? labels.working : `${count} ${count === 1 ? labels.step : labels.steps}`\n return <div className=\"text-sm\"><button type=\"button\" onClick={() => setOpen((value) => !value)} aria-expanded={open} className=\"flex h-7 max-w-full items-center gap-1.5 rounded-md px-1.5 text-left text-xs text-fg-muted transition-colors hover:bg-surface-hover hover:text-fg\"><span className={cn('size-1.5 shrink-0 rounded-full', running ? 'animate-pulse bg-primary' : errored ? 'bg-danger' : 'bg-success')} /><span className=\"shrink-0 font-medium\">{status}</span><span aria-hidden className=\"text-fg-subtle\">·</span><span className=\"min-w-0 truncate\">{toolLabels?.[latestName] ?? latestName.replaceAll('_', ' ')}</span><ChevronRight size={12} className={cn('shrink-0 text-fg-subtle transition-transform', open && 'rotate-90')} /></button>{open ? <div className=\"mt-1.5 space-y-1.5 border-l border-border pl-2\">{parts.map((part, index) => { const name = agentToolName(part); return <AgentToolCard key={`${name}-${index}`} name={name} label={toolLabels?.[name]} state={String(part.state ?? 'output-available')} input={part.input} output={part.output} inputLabel={labels.input} resultLabel={labels.result} /> })}</div> : null}</div>\n}\n\nexport function ChatMarkdown({ children }: { children: string }) {\n return <div className=\"space-y-2 text-sm leading-relaxed text-fg\"><Markdown remarkPlugins={[remarkGfm]} components={{ p: ({ children: content }) => <p className=\"whitespace-pre-wrap\">{content}</p>, h1: ({ children: content }) => <h1 className=\"text-lg font-semibold\">{content}</h1>, h2: ({ children: content }) => <h2 className=\"text-base font-semibold\">{content}</h2>, ul: ({ children: content }) => <ul className=\"list-disc space-y-1 pl-5\">{content}</ul>, ol: ({ children: content }) => <ol className=\"list-decimal space-y-1 pl-5\">{content}</ol>, code: ({ children: content }) => <code className=\"rounded bg-bg-subtle px-1 py-0.5 font-mono text-[0.85em] text-primary\">{content}</code>, pre: ({ children: content }) => <pre className=\"overflow-auto rounded-lg bg-overlay p-3 text-sm whitespace-pre-wrap text-white\">{content}</pre>, table: ({ children: content }) => <div className=\"overflow-x-auto\"><table className=\"w-full border-collapse text-sm\">{content}</table></div>, th: ({ children: content }) => <th className=\"border-b border-border px-2 py-1 text-left\">{content}</th>, td: ({ children: content }) => <td className=\"border-b border-border-subtle px-2 py-1\">{content}</td>, a: ({ href, children: content }) => href?.startsWith('/') ? <UiLink href={href} className=\"font-medium text-primary underline-offset-2 hover:underline\">{content}</UiLink> : <a href={href} target=\"_blank\" rel=\"noreferrer\" className=\"font-medium text-primary underline-offset-2 hover:underline\">{content}</a> }}>{children}</Markdown></div>\n}\n\nexport function AgentToolCard({ name, label, state, input, output, inputLabel = 'Input', resultLabel = 'Result' }: { name: string; label?: string; state: string; input?: unknown; output?: unknown; inputLabel?: string; resultLabel?: string }) {\n const [open, setOpen] = React.useState(false)\n const running = state === 'input-streaming' || state === 'input-available'\n const errored = state === 'output-error' || (output as { ok?: boolean } | undefined)?.ok === false\n return <div className=\"overflow-hidden rounded-lg border border-border bg-bg-subtle text-sm\"><button type=\"button\" onClick={() => setOpen((value) => !value)} className=\"flex w-full items-center gap-2.5 px-3 py-2 text-left transition-colors hover:bg-surface-hover\"><span className={cn('flex size-6 shrink-0 items-center justify-center rounded-md', errored ? 'bg-danger-subtle text-danger' : 'bg-primary-subtle text-primary')}><Database size={14} /></span><span className=\"min-w-0 flex-1 truncate font-medium text-fg\">{label ?? name.replaceAll('_', ' ')}</span>{running ? <Loader2 size={14} className=\"animate-spin text-fg-subtle\" /> : errored ? <AlertCircle size={14} className=\"text-danger\" /> : <CheckCircle2 size={14} className=\"text-success\" />}<ChevronRight size={14} className={cn('text-fg-subtle transition-transform', open && 'rotate-90')} /></button>{open ? <div className=\"space-y-2 border-t border-border px-3 py-2\">{input !== undefined ? <AgentToolDetail label={inputLabel} value={input} /> : null}{output !== undefined ? <AgentToolDetail label={resultLabel} value={output} /> : null}</div> : null}</div>\n}\n\nfunction AgentToolDetail({ label, value }: { label: string; value: unknown }) {\n let text: string\n try { text = JSON.stringify(value, null, 2) } catch { text = String(value) }\n return <div><div className=\"mb-1 text-[11px] font-semibold tracking-wide text-fg-subtle uppercase\">{label}</div><pre className=\"max-h-60 overflow-auto rounded-md bg-surface p-2 text-xs leading-relaxed text-fg ring-1 ring-border\">{text}</pre></div>\n}\n"]}