@kubuild/ai 0.3.0 → 0.4.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/dist/{chunk-27AY2KBI.js → chunk-3DRAVYU3.js} +173 -46
- package/dist/chunk-3DRAVYU3.js.map +1 -0
- package/dist/chunk-ILLNRABB.js +24 -0
- package/dist/chunk-ILLNRABB.js.map +1 -0
- package/dist/{chunk-JY4NHD3Q.js → chunk-T72YWBJS.js} +62 -1
- package/dist/chunk-T72YWBJS.js.map +1 -0
- package/dist/{chunk-UH24LHRY.js → chunk-YETTTWHI.js} +226 -1
- package/dist/chunk-YETTTWHI.js.map +1 -0
- package/dist/client/ai-client.d.ts +20 -1
- package/dist/client/ai-client.d.ts.map +1 -1
- package/dist/client/index.cjs +61 -0
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.js +1 -1
- package/dist/core/document-outline.d.ts +60 -0
- package/dist/core/document-outline.d.ts.map +1 -0
- package/dist/core/index.d.ts +2 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/messages.d.ts +18 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/prompt-compiler.d.ts +29 -0
- package/dist/core/prompt-compiler.d.ts.map +1 -1
- package/dist/index.cjs +261 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +31 -3
- package/dist/react/index.cjs +173 -0
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +112 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/use-ai-agent.d.ts +47 -0
- package/dist/react/use-ai-agent.d.ts.map +1 -0
- package/dist/server/adapters/anthropic.d.ts.map +1 -1
- package/dist/server/adapters/custom.d.ts.map +1 -1
- package/dist/server/adapters/gemini.d.ts.map +1 -1
- package/dist/server/adapters/index.cjs +180 -45
- package/dist/server/adapters/index.cjs.map +1 -1
- package/dist/server/adapters/index.js +2 -1
- package/dist/server/adapters/openai.d.ts +25 -0
- package/dist/server/adapters/openai.d.ts.map +1 -1
- package/dist/server/agent.d.ts +59 -0
- package/dist/server/agent.d.ts.map +1 -0
- package/dist/server/engine.d.ts.map +1 -1
- package/dist/server/handler.d.ts +10 -1
- package/dist/server/handler.d.ts.map +1 -1
- package/dist/server/index.cjs +1734 -260
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1229 -94
- package/dist/server/index.js.map +1 -1
- package/dist/server/tools/helpers.d.ts +66 -0
- package/dist/server/tools/helpers.d.ts.map +1 -0
- package/dist/server/tools/index.d.ts +25 -0
- package/dist/server/tools/index.d.ts.map +1 -0
- package/dist/server/tools/read-tools.d.ts +3 -0
- package/dist/server/tools/read-tools.d.ts.map +1 -0
- package/dist/server/tools/types.d.ts +51 -0
- package/dist/server/tools/types.d.ts.map +1 -0
- package/dist/server/tools/write-tools.d.ts +3 -0
- package/dist/server/tools/write-tools.d.ts.map +1 -0
- package/dist/types.d.ts +193 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
- package/dist/chunk-27AY2KBI.js.map +0 -1
- package/dist/chunk-JY4NHD3Q.js.map +0 -1
- package/dist/chunk-UH24LHRY.js.map +0 -1
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.ts","../../src/react/use-ai-generator.ts","../../src/client/ai-client.ts","../../src/react/use-ai-chat.ts"],"sourcesContent":["export * from './use-ai-generator';\nexport * from './use-ai-chat';\n\n","import { useState, useCallback, useRef } from 'react';\nimport type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiStreamCallbacks,\n} from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\nexport interface UseAiGeneratorOptions extends AiClientOptions {\n onSuccess?: (data: PageDocument | Node) => void;\n onError?: (error: Error) => void;\n}\n\nexport function useAiGenerator(options: UseAiGeneratorOptions) {\n const [isGenerating, setIsGenerating] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [currentStep, setCurrentStep] = useState<string>('');\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n\n // Cancel any in-flight request\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n }\n }, []);\n\n /**\n * Standard full page generator (non-streaming).\n */\n const generatePage = useCallback(\n async (params: AiGeneratePageRequest): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating full page...');\n\n try {\n const doc = await clientRef.current.generatePage(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(doc);\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each step (onStatus, onMetadata, onSection, onComplete).\n */\n const streamPage = useCallback(\n async (\n params: AiGeneratePageRequest,\n streamCallbacks?: AiStreamCallbacks,\n ): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(true);\n setError(null);\n setCurrentStep('Connecting to AI stream...');\n\n try {\n const doc = await clientRef.current.streamPage(\n params,\n {\n onStatus: (msg) => {\n setCurrentStep(msg);\n streamCallbacks?.onStatus?.(msg);\n },\n onMetadata: (metadata, rootPage) => {\n streamCallbacks?.onMetadata?.(metadata, rootPage);\n },\n onSection: (section, index, total) => {\n setCurrentStep(`Section ${index + 1}/${total} rendered`);\n streamCallbacks?.onSection?.(section, index, total);\n },\n onComplete: (completedDoc) => {\n setCurrentStep('Completed');\n streamCallbacks?.onComplete?.(completedDoc);\n options.onSuccess?.(completedDoc);\n },\n onError: (err) => {\n setError(err);\n streamCallbacks?.onError?.(err);\n options.onError?.(err);\n },\n },\n { signal: ac.signal },\n );\n\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const generateSection = useCallback(\n async (params: AiGenerateSectionRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating section...');\n\n try {\n const node = await clientRef.current.generateSection(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const refactorNode = useCallback(\n async (params: AiRefactorNodeRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Refactoring node...');\n\n try {\n const node = await clientRef.current.refactorNode(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n return {\n generatePage,\n streamPage,\n generateSection,\n refactorNode,\n isGenerating,\n isStreaming,\n currentStep,\n error,\n cancel,\n };\n}\n","import type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiChatRequest,\n AiChatResponse,\n AiChatMessage,\n AiGenerateResponse,\n AiStreamCallbacks,\n AiStreamEvent,\n} from '../types';\n\nexport interface AiClientOptions {\n endpoint: string;\n headers?:\n | Record<string, string>\n | (() => Promise<Record<string, string>> | Record<string, string>);\n fetch?: typeof fetch;\n}\n\nexport class KubuildAiClient {\n private endpoint: string;\n private headers?: AiClientOptions['headers'];\n private fetchFn: typeof fetch;\n\n constructor(options: AiClientOptions) {\n if (!options.endpoint) {\n throw new Error('KubuildAiClient requires an endpoint URL');\n }\n this.endpoint = options.endpoint;\n this.headers = options.headers;\n this.fetchFn = options.fetch || globalThis.fetch.bind(globalThis);\n }\n\n private async resolveHeaders(): Promise<Record<string, string>> {\n if (!this.headers) return {};\n if (typeof this.headers === 'function') {\n return await this.headers();\n }\n return this.headers;\n }\n\n private async sendRequest<T>(\n body: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<T> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify(body),\n signal,\n });\n\n const json = (await res.json()) as AiGenerateResponse<T>;\n\n if (!res.ok || !json.success || !json.data) {\n const errMessage = json?.error?.message || `HTTP ${res.status}: ${res.statusText}`;\n const error = new Error(errMessage);\n (error as unknown as { code?: string; details?: unknown }).code = json?.error?.code;\n (error as unknown as { code?: string; details?: unknown }).details = json?.error?.details;\n throw error;\n }\n\n return json.data;\n }\n\n /**\n * Standard single-pass full page generator (non-streaming).\n */\n async generatePage(\n params: AiGeneratePageRequest,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n return this.sendRequest<PageDocument>(\n {\n mode: 'full-page',\n ...params,\n },\n options?.signal,\n );\n }\n\n /**\n * Low-level SSE frame consumer shared by every streaming method on this client\n * (`streamPage`, `chatStream`) — one transport implementation, reused rather than\n * duplicated per stream \"flavor\" (STORA-515).\n *\n * Reads `res.body`, decodes `data: <json>\\n\\n` frames into `AiStreamEvent`s, and\n * invokes `onEvent` for each one in arrival order. If `onEvent` throws (the\n * conventional way callers signal a fatal `error` event), the throw propagates out of\n * this method after the reader lock is released.\n */\n private async consumeSse(\n res: Response,\n onEvent: (event: AiStreamEvent) => void,\n ): Promise<void> {\n if (!res.body) {\n throw new Error('Response body is null, cannot stream');\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop() || '';\n\n for (const part of parts) {\n const lines = part.split('\\n');\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n let event: AiStreamEvent;\n try {\n event = JSON.parse(line.slice(6).trim()) as AiStreamEvent;\n } catch {\n // Ignore incomplete or non-JSON data frames\n continue;\n }\n onEvent(event);\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each event (status, metadata, section, complete).\n */\n async streamPage(\n params: AiGeneratePageRequest,\n callbacks?: AiStreamCallbacks,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'full-page',\n stream: true,\n ...params,\n }),\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalDoc: PageDocument | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'status') {\n callbacks?.onStatus?.(event.message);\n } else if (event.type === 'metadata') {\n callbacks?.onMetadata?.(event.metadata, event.rootPageNode);\n } else if (event.type === 'section') {\n callbacks?.onSection?.(event.section, event.index, event.total);\n } else if (event.type === 'complete') {\n finalDoc = event.document;\n callbacks?.onComplete?.(event.document);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalDoc) {\n throw new Error('Stream ended without receiving complete document');\n }\n\n return finalDoc;\n }\n\n /**\n * Token-level streaming chat (STORA-515). Calls `onChatChunk` for every partial chunk\n * as it arrives and resolves with the fully assembled assistant `AiChatMessage` once\n * the stream's terminal `chat-complete` event is received. Reuses the exact same SSE\n * transport as `streamPage` via `consumeSse` — no separate parsing path.\n */\n async chatStream(\n params: AiChatRequest,\n callbacks?: Pick<AiStreamCallbacks, 'onChatChunk' | 'onChatComplete' | 'onError'>,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatMessage> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'chat',\n stream: true,\n ...params,\n }),\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Chat streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalMessage: AiChatMessage | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'chat-chunk') {\n callbacks?.onChatChunk?.(event.delta, event.content);\n } else if (event.type === 'chat-complete') {\n finalMessage = event.message;\n callbacks?.onChatComplete?.(event.message);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalMessage) {\n throw new Error('Chat stream ended without receiving a complete message');\n }\n\n return finalMessage;\n }\n\n async generateSection(\n params: AiGenerateSectionRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'section',\n ...params,\n },\n options?.signal,\n );\n }\n\n async refactorNode(\n params: AiRefactorNodeRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'refactor',\n ...params,\n },\n options?.signal,\n );\n }\n\n async chat(\n params: AiChatRequest,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatResponse> {\n return this.sendRequest<AiChatResponse>(\n {\n mode: 'chat',\n ...params,\n },\n options?.signal,\n );\n }\n}\n\nexport function createAiClient(options: AiClientOptions): KubuildAiClient {\n return new KubuildAiClient(options);\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AiChatMessage, AiChatResponse } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/**\n * Optional, host-implemented chat history persistence (STORA-520). `@kubuild/ai` stays\n * storage-agnostic — it never imports `localStorage` or any concrete storage mechanism\n * itself; the host supplies an adapter backed by whatever it wants (browser\n * `localStorage`, IndexedDB, a Stora.page backend call, etc.).\n */\nexport interface AiChatHistoryStorageAdapter {\n /** Called once on mount to seed `messages` before the user sends anything. */\n loadHistory(): Promise<AiChatMessage[]> | AiChatMessage[];\n /**\n * Called at completed-message boundaries (a user message just sent, or an assistant\n * reply that just finished/streamed to completion) — never on every mid-stream token,\n * see `useAiChat`'s persistence notes.\n */\n saveHistory(messages: AiChatMessage[]): Promise<void> | void;\n}\n\nexport interface UseAiChatOptions extends AiClientOptions {\n initialMessages?: AiChatMessage[];\n onMessage?: (message: AiChatMessage) => void;\n onError?: (error: Error) => void;\n /**\n * Fired for every partial token/chunk of a streaming chat response (STORA-516),\n * mirroring `useAiGenerator`'s `AiStreamCallbacks` naming convention. Optional — the\n * `messages` array already updates incrementally on its own, this is only for hosts\n * that want a side-channel (e.g. custom rendering, telemetry) into the raw chunks.\n */\n onChunk?: (delta: string, content: string) => void;\n /**\n * Optional storage adapter (STORA-520) used to persist/restore chat history across\n * reloads. Without it, behavior is exactly as before: pure in-memory React state, lost\n * on reload/unmount. `loadHistory()` is called once on mount to hydrate `messages`\n * (only overriding `initialMessages` when it resolves a non-empty array);\n * `saveHistory()` is called at completed-message boundaries — after a user message is\n * appended, and after an assistant reply finishes (streamed or not) — intentionally\n * *not* on every streaming chunk, to avoid hammering the adapter mid-stream.\n */\n historyStorage?: AiChatHistoryStorageAdapter;\n}\n\nexport interface SendMessageOptions {\n currentDocument?: PageDocument;\n selectedNodeId?: string;\n systemPrompt?: string;\n /**\n * Opt out of token-level streaming for this call (STORA-515/516 default to `true`).\n * When `false`, behaves exactly like the pre-streaming single request/response call.\n */\n stream?: boolean;\n}\n\nexport function useAiChat(options: UseAiChatOptions) {\n const [messages, setMessages] = useState<AiChatMessage[]>(options.initialMessages || []);\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const historyStorageRef = useRef(options.historyStorage);\n historyStorageRef.current = options.historyStorage;\n\n /**\n * STORA-520 — best-effort persistence: a storage failure (quota exceeded, network\n * error on a remote adapter, etc.) must never break the chat itself, so errors are\n * swallowed here rather than surfaced through `setError`/`onError` (those are reserved\n * for the actual chat request failing).\n */\n const persistHistory = useCallback((history: AiChatMessage[]) => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n try {\n void Promise.resolve(adapter.saveHistory(history)).catch(() => undefined);\n } catch {\n // Synchronous throw from a non-Promise-returning adapter — ignore, same reasoning.\n }\n }, []);\n\n // STORA-520 — hydrate from storage once on mount. Only overrides `initialMessages`\n // when the adapter actually resolves a non-empty history, so a fresh/empty adapter\n // (e.g. first-ever visit) doesn't clobber a host-provided `initialMessages` seed.\n useEffect(() => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n let cancelled = false;\n Promise.resolve(adapter.loadHistory())\n .then((loaded) => {\n if (!cancelled && Array.isArray(loaded) && loaded.length > 0) {\n setMessages(loaded);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const e = err instanceof Error ? err : new Error(String(err));\n options.onError?.(e);\n });\n return () => {\n cancelled = true;\n };\n // Deliberately empty deps: `historyStorage`/`onError` are read via refs/closures at\n // call time and this must run exactly once per mount, not whenever the caller passes\n // a fresh options object.\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n // Aborts the underlying fetch (and, for a streaming request, the in-flight\n // ReadableStream read) — not just local UI state (STORA-516 AC).\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n }\n }, []);\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n persistHistory([]);\n }, [persistHistory]);\n\n const sendMessage = useCallback(\n async (content: string, sendOptions?: SendMessageOptions): Promise<AiChatMessage | null> => {\n if (!content.trim()) return null;\n\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n const userMessage: AiChatMessage = {\n role: 'user',\n content: content.trim(),\n timestamp: Date.now(),\n };\n\n const updatedHistory = [...messages, userMessage];\n setMessages(updatedHistory);\n setIsLoading(true);\n setError(null);\n // Completed-message boundary #1 (STORA-520): the user's message is final the\n // instant it's sent — persist it now rather than waiting for the assistant reply,\n // so a reload mid-request doesn't lose it.\n persistHistory(updatedHistory);\n\n const useStream = sendOptions?.stream !== false;\n\n try {\n if (useStream) {\n setIsStreaming(true);\n\n // Placeholder assistant message, filled in incrementally as chunks arrive.\n let assistantIndex = -1;\n setMessages((prev) => {\n assistantIndex = prev.length;\n return [...prev, { role: 'assistant', content: '', timestamp: Date.now() }];\n });\n\n const finalMessage = await clientRef.current.chatStream(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n {\n onChatChunk: (delta, contentSoFar) => {\n options.onChunk?.(delta, contentSoFar);\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = { ...next[assistantIndex], content: contentSoFar };\n return next;\n });\n },\n onChatComplete: (message) => {\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = message;\n return next;\n });\n },\n },\n { signal: ac.signal },\n );\n\n // Completed-message boundary #2 (STORA-520): the stream finished — persist the\n // fully assembled assistant message, never the intermediate per-chunk content\n // (`onChatChunk` above deliberately does not call `persistHistory`).\n persistHistory([...updatedHistory, finalMessage]);\n options.onMessage?.(finalMessage);\n return finalMessage;\n }\n\n const response: AiChatResponse = await clientRef.current.chat(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n { signal: ac.signal },\n );\n\n const assistantMsg = response.message;\n setMessages((prev) => [...prev, assistantMsg]);\n // Completed-message boundary #2 (non-streaming variant): a single request/response\n // call has no intermediate chunks at all, so this is simply \"after the reply\".\n persistHistory([...updatedHistory, assistantMsg]);\n options.onMessage?.(assistantMsg);\n return assistantMsg;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsLoading(false);\n setIsStreaming(false);\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, messages, options, persistHistory],\n );\n\n return {\n messages,\n sendMessage,\n isLoading,\n /** Whether the in-flight `sendMessage` call is a token-level stream (STORA-516). */\n isStreaming,\n error,\n cancel,\n clearMessages,\n setMessages,\n };\n}\n"],"mappings":"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","names":["import_react"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.ts","../../src/react/use-ai-generator.ts","../../src/client/ai-client.ts","../../src/react/use-ai-chat.ts","../../src/react/use-ai-agent.ts"],"sourcesContent":["export * from './use-ai-generator';\nexport * from './use-ai-chat';\n\nexport * from './use-ai-agent';\n","import { useState, useCallback, useRef } from 'react';\nimport type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiStreamCallbacks,\n} from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\nexport interface UseAiGeneratorOptions extends AiClientOptions {\n onSuccess?: (data: PageDocument | Node) => void;\n onError?: (error: Error) => void;\n}\n\nexport function useAiGenerator(options: UseAiGeneratorOptions) {\n const [isGenerating, setIsGenerating] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [currentStep, setCurrentStep] = useState<string>('');\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n\n // Cancel any in-flight request\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n }\n }, []);\n\n /**\n * Standard full page generator (non-streaming).\n */\n const generatePage = useCallback(\n async (params: AiGeneratePageRequest): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating full page...');\n\n try {\n const doc = await clientRef.current.generatePage(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(doc);\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each step (onStatus, onMetadata, onSection, onComplete).\n */\n const streamPage = useCallback(\n async (\n params: AiGeneratePageRequest,\n streamCallbacks?: AiStreamCallbacks,\n ): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(true);\n setError(null);\n setCurrentStep('Connecting to AI stream...');\n\n try {\n const doc = await clientRef.current.streamPage(\n params,\n {\n onStatus: (msg) => {\n setCurrentStep(msg);\n streamCallbacks?.onStatus?.(msg);\n },\n onMetadata: (metadata, rootPage) => {\n streamCallbacks?.onMetadata?.(metadata, rootPage);\n },\n onSection: (section, index, total) => {\n setCurrentStep(`Section ${index + 1}/${total} rendered`);\n streamCallbacks?.onSection?.(section, index, total);\n },\n onComplete: (completedDoc) => {\n setCurrentStep('Completed');\n streamCallbacks?.onComplete?.(completedDoc);\n options.onSuccess?.(completedDoc);\n },\n onError: (err) => {\n setError(err);\n streamCallbacks?.onError?.(err);\n options.onError?.(err);\n },\n },\n { signal: ac.signal },\n );\n\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const generateSection = useCallback(\n async (params: AiGenerateSectionRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating section...');\n\n try {\n const node = await clientRef.current.generateSection(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const refactorNode = useCallback(\n async (params: AiRefactorNodeRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Refactoring node...');\n\n try {\n const node = await clientRef.current.refactorNode(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n return {\n generatePage,\n streamPage,\n generateSection,\n refactorNode,\n isGenerating,\n isStreaming,\n currentStep,\n error,\n cancel,\n };\n}\n","import type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiAgentRequest,\n AiAgentRunResult,\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiChatRequest,\n AiChatResponse,\n AiChatMessage,\n AiGenerateResponse,\n AiStreamCallbacks,\n AiStreamEvent,\n} from '../types';\n\nexport interface AiClientOptions {\n endpoint: string;\n headers?:\n | Record<string, string>\n | (() => Promise<Record<string, string>> | Record<string, string>);\n fetch?: typeof fetch;\n /**\n * Forwarded to every request (STORA-530). Hosts whose AI endpoint authenticates with\n * HttpOnly cookies rather than a bearer header need `'include'` here — cross-origin\n * `fetch` omits cookies by default, so without it the proxy endpoint sees an\n * unauthenticated request no matter what the browser has stored.\n */\n credentials?: RequestCredentials;\n}\n\nexport class KubuildAiClient {\n private endpoint: string;\n private headers?: AiClientOptions['headers'];\n private fetchFn: typeof fetch;\n private credentials?: RequestCredentials;\n\n constructor(options: AiClientOptions) {\n if (!options.endpoint) {\n throw new Error('KubuildAiClient requires an endpoint URL');\n }\n this.endpoint = options.endpoint;\n this.headers = options.headers;\n this.fetchFn = options.fetch || globalThis.fetch.bind(globalThis);\n this.credentials = options.credentials;\n }\n\n private async resolveHeaders(): Promise<Record<string, string>> {\n if (!this.headers) return {};\n if (typeof this.headers === 'function') {\n return await this.headers();\n }\n return this.headers;\n }\n\n private async sendRequest<T>(\n body: Record<string, unknown>,\n signal?: AbortSignal,\n ): Promise<T> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify(body),\n credentials: this.credentials,\n signal,\n });\n\n const json = (await res.json()) as AiGenerateResponse<T>;\n\n if (!res.ok || !json.success || !json.data) {\n const errMessage = json?.error?.message || `HTTP ${res.status}: ${res.statusText}`;\n const error = new Error(errMessage);\n (error as unknown as { code?: string; details?: unknown }).code = json?.error?.code;\n (error as unknown as { code?: string; details?: unknown }).details = json?.error?.details;\n throw error;\n }\n\n return json.data;\n }\n\n /**\n * Standard single-pass full page generator (non-streaming).\n */\n async generatePage(\n params: AiGeneratePageRequest,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n return this.sendRequest<PageDocument>(\n {\n mode: 'full-page',\n ...params,\n },\n options?.signal,\n );\n }\n\n /**\n * Low-level SSE frame consumer shared by every streaming method on this client\n * (`streamPage`, `chatStream`) — one transport implementation, reused rather than\n * duplicated per stream \"flavor\" (STORA-515).\n *\n * Reads `res.body`, decodes `data: <json>\\n\\n` frames into `AiStreamEvent`s, and\n * invokes `onEvent` for each one in arrival order. If `onEvent` throws (the\n * conventional way callers signal a fatal `error` event), the throw propagates out of\n * this method after the reader lock is released.\n */\n private async consumeSse(\n res: Response,\n onEvent: (event: AiStreamEvent) => void,\n ): Promise<void> {\n if (!res.body) {\n throw new Error('Response body is null, cannot stream');\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop() || '';\n\n for (const part of parts) {\n const lines = part.split('\\n');\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n let event: AiStreamEvent;\n try {\n event = JSON.parse(line.slice(6).trim()) as AiStreamEvent;\n } catch {\n // Ignore incomplete or non-JSON data frames\n continue;\n }\n onEvent(event);\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each event (status, metadata, section, complete).\n */\n async streamPage(\n params: AiGeneratePageRequest,\n callbacks?: AiStreamCallbacks,\n options?: { signal?: AbortSignal },\n ): Promise<PageDocument> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'full-page',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalDoc: PageDocument | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'status') {\n callbacks?.onStatus?.(event.message);\n } else if (event.type === 'metadata') {\n callbacks?.onMetadata?.(event.metadata, event.rootPageNode);\n } else if (event.type === 'section') {\n callbacks?.onSection?.(event.section, event.index, event.total);\n } else if (event.type === 'complete') {\n finalDoc = event.document;\n callbacks?.onComplete?.(event.document);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalDoc) {\n throw new Error('Stream ended without receiving complete document');\n }\n\n return finalDoc;\n }\n\n /**\n * Token-level streaming chat (STORA-515). Calls `onChatChunk` for every partial chunk\n * as it arrives and resolves with the fully assembled assistant `AiChatMessage` once\n * the stream's terminal `chat-complete` event is received. Reuses the exact same SSE\n * transport as `streamPage` via `consumeSse` — no separate parsing path.\n */\n async chatStream(\n params: AiChatRequest,\n callbacks?: Pick<AiStreamCallbacks, 'onChatChunk' | 'onChatComplete' | 'onError'>,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatMessage> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'chat',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Chat streaming failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalMessage: AiChatMessage | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'chat-chunk') {\n callbacks?.onChatChunk?.(event.delta, event.content);\n } else if (event.type === 'chat-complete') {\n finalMessage = event.message;\n callbacks?.onChatComplete?.(event.message);\n } else if (event.type === 'error') {\n const err = new Error(event.error.message || 'Stream error');\n (err as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(err);\n throw err;\n }\n });\n\n if (!finalMessage) {\n throw new Error('Chat stream ended without receiving a complete message');\n }\n\n return finalMessage;\n }\n\n /**\n * Runs an agent turn over SSE (STORA-530).\n *\n * Streams the agent's progress — each reasoning step, each tool call and its result —\n * through `callbacks`, and resolves with the run's `AiAgentRunResult`. The resolved value\n * carries the `ops` the editor replays; nothing is applied to any document by this call.\n * Reuses the same `consumeSse` transport as `streamPage`/`chatStream`.\n */\n async runAgent(\n params: AiAgentRequest,\n callbacks?: Pick<\n AiStreamCallbacks,\n 'onAgentStep' | 'onToolCall' | 'onToolResult' | 'onAgentComplete' | 'onError'\n >,\n options?: { signal?: AbortSignal },\n ): Promise<AiAgentRunResult> {\n const dynamicHeaders = await this.resolveHeaders();\n\n const res = await this.fetchFn(this.endpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...dynamicHeaders,\n },\n body: JSON.stringify({\n mode: 'agent',\n stream: true,\n ...params,\n }),\n credentials: this.credentials,\n signal: options?.signal,\n });\n\n if (!res.ok) {\n const errText = await res.text().catch(() => '');\n const err = new Error(`Agent run failed [${res.status} ${res.statusText}]: ${errText}`);\n callbacks?.onError?.(err);\n throw err;\n }\n\n let finalResult: AiAgentRunResult | null = null;\n let streamError: Error | null = null;\n\n await this.consumeSse(res, (event) => {\n if (event.type === 'agent-step') {\n callbacks?.onAgentStep?.(event.step, event.maxSteps);\n } else if (event.type === 'tool-call') {\n callbacks?.onToolCall?.({ id: event.id, name: event.name, input: event.input });\n } else if (event.type === 'tool-result') {\n callbacks?.onToolResult?.({\n id: event.id,\n name: event.name,\n ok: event.ok,\n summary: event.summary,\n });\n } else if (event.type === 'agent-complete') {\n finalResult = event.result;\n callbacks?.onAgentComplete?.(event.result);\n } else if (event.type === 'error') {\n // The agent emits `agent-complete` before `error`, so ops produced before a\n // mid-run failure survive. Record the error instead of throwing out of the SSE\n // reader, and let the terminal result (if any) decide what the caller sees.\n streamError = new Error(event.error.message || 'Agent error');\n (streamError as unknown as { code?: string }).code = event.error.code;\n callbacks?.onError?.(streamError);\n }\n });\n\n if (finalResult) return finalResult;\n throw streamError ?? new Error('Agent stream ended without a result');\n }\n\n async generateSection(\n params: AiGenerateSectionRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'section',\n ...params,\n },\n options?.signal,\n );\n }\n\n async refactorNode(\n params: AiRefactorNodeRequest,\n options?: { signal?: AbortSignal },\n ): Promise<Node> {\n return this.sendRequest<Node>(\n {\n mode: 'refactor',\n ...params,\n },\n options?.signal,\n );\n }\n\n async chat(\n params: AiChatRequest,\n options?: { signal?: AbortSignal },\n ): Promise<AiChatResponse> {\n return this.sendRequest<AiChatResponse>(\n {\n mode: 'chat',\n ...params,\n },\n options?.signal,\n );\n }\n}\n\nexport function createAiClient(options: AiClientOptions): KubuildAiClient {\n return new KubuildAiClient(options);\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AiChatMessage, AiChatResponse } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/**\n * Optional, host-implemented chat history persistence (STORA-520). `@kubuild/ai` stays\n * storage-agnostic — it never imports `localStorage` or any concrete storage mechanism\n * itself; the host supplies an adapter backed by whatever it wants (browser\n * `localStorage`, IndexedDB, a Stora.page backend call, etc.).\n */\nexport interface AiChatHistoryStorageAdapter {\n /** Called once on mount to seed `messages` before the user sends anything. */\n loadHistory(): Promise<AiChatMessage[]> | AiChatMessage[];\n /**\n * Called at completed-message boundaries (a user message just sent, or an assistant\n * reply that just finished/streamed to completion) — never on every mid-stream token,\n * see `useAiChat`'s persistence notes.\n */\n saveHistory(messages: AiChatMessage[]): Promise<void> | void;\n}\n\nexport interface UseAiChatOptions extends AiClientOptions {\n initialMessages?: AiChatMessage[];\n onMessage?: (message: AiChatMessage) => void;\n onError?: (error: Error) => void;\n /**\n * Fired for every partial token/chunk of a streaming chat response (STORA-516),\n * mirroring `useAiGenerator`'s `AiStreamCallbacks` naming convention. Optional — the\n * `messages` array already updates incrementally on its own, this is only for hosts\n * that want a side-channel (e.g. custom rendering, telemetry) into the raw chunks.\n */\n onChunk?: (delta: string, content: string) => void;\n /**\n * Optional storage adapter (STORA-520) used to persist/restore chat history across\n * reloads. Without it, behavior is exactly as before: pure in-memory React state, lost\n * on reload/unmount. `loadHistory()` is called once on mount to hydrate `messages`\n * (only overriding `initialMessages` when it resolves a non-empty array);\n * `saveHistory()` is called at completed-message boundaries — after a user message is\n * appended, and after an assistant reply finishes (streamed or not) — intentionally\n * *not* on every streaming chunk, to avoid hammering the adapter mid-stream.\n */\n historyStorage?: AiChatHistoryStorageAdapter;\n}\n\nexport interface SendMessageOptions {\n currentDocument?: PageDocument;\n selectedNodeId?: string;\n systemPrompt?: string;\n /**\n * Opt out of token-level streaming for this call (STORA-515/516 default to `true`).\n * When `false`, behaves exactly like the pre-streaming single request/response call.\n */\n stream?: boolean;\n}\n\nexport function useAiChat(options: UseAiChatOptions) {\n const [messages, setMessages] = useState<AiChatMessage[]>(options.initialMessages || []);\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const historyStorageRef = useRef(options.historyStorage);\n historyStorageRef.current = options.historyStorage;\n\n /**\n * STORA-520 — best-effort persistence: a storage failure (quota exceeded, network\n * error on a remote adapter, etc.) must never break the chat itself, so errors are\n * swallowed here rather than surfaced through `setError`/`onError` (those are reserved\n * for the actual chat request failing).\n */\n const persistHistory = useCallback((history: AiChatMessage[]) => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n try {\n void Promise.resolve(adapter.saveHistory(history)).catch(() => undefined);\n } catch {\n // Synchronous throw from a non-Promise-returning adapter — ignore, same reasoning.\n }\n }, []);\n\n // STORA-520 — hydrate from storage once on mount. Only overrides `initialMessages`\n // when the adapter actually resolves a non-empty history, so a fresh/empty adapter\n // (e.g. first-ever visit) doesn't clobber a host-provided `initialMessages` seed.\n useEffect(() => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n let cancelled = false;\n Promise.resolve(adapter.loadHistory())\n .then((loaded) => {\n if (!cancelled && Array.isArray(loaded) && loaded.length > 0) {\n setMessages(loaded);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const e = err instanceof Error ? err : new Error(String(err));\n options.onError?.(e);\n });\n return () => {\n cancelled = true;\n };\n // Deliberately empty deps: `historyStorage`/`onError` are read via refs/closures at\n // call time and this must run exactly once per mount, not whenever the caller passes\n // a fresh options object.\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n // Aborts the underlying fetch (and, for a streaming request, the in-flight\n // ReadableStream read) — not just local UI state (STORA-516 AC).\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n }\n }, []);\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n persistHistory([]);\n }, [persistHistory]);\n\n const sendMessage = useCallback(\n async (content: string, sendOptions?: SendMessageOptions): Promise<AiChatMessage | null> => {\n if (!content.trim()) return null;\n\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n const userMessage: AiChatMessage = {\n role: 'user',\n content: content.trim(),\n timestamp: Date.now(),\n };\n\n const updatedHistory = [...messages, userMessage];\n setMessages(updatedHistory);\n setIsLoading(true);\n setError(null);\n // Completed-message boundary #1 (STORA-520): the user's message is final the\n // instant it's sent — persist it now rather than waiting for the assistant reply,\n // so a reload mid-request doesn't lose it.\n persistHistory(updatedHistory);\n\n const useStream = sendOptions?.stream !== false;\n\n try {\n if (useStream) {\n setIsStreaming(true);\n\n // Placeholder assistant message, filled in incrementally as chunks arrive.\n let assistantIndex = -1;\n setMessages((prev) => {\n assistantIndex = prev.length;\n return [...prev, { role: 'assistant', content: '', timestamp: Date.now() }];\n });\n\n const finalMessage = await clientRef.current.chatStream(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n {\n onChatChunk: (delta, contentSoFar) => {\n options.onChunk?.(delta, contentSoFar);\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = { ...next[assistantIndex], content: contentSoFar };\n return next;\n });\n },\n onChatComplete: (message) => {\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = message;\n return next;\n });\n },\n },\n { signal: ac.signal },\n );\n\n // Completed-message boundary #2 (STORA-520): the stream finished — persist the\n // fully assembled assistant message, never the intermediate per-chunk content\n // (`onChatChunk` above deliberately does not call `persistHistory`).\n persistHistory([...updatedHistory, finalMessage]);\n options.onMessage?.(finalMessage);\n return finalMessage;\n }\n\n const response: AiChatResponse = await clientRef.current.chat(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n { signal: ac.signal },\n );\n\n const assistantMsg = response.message;\n setMessages((prev) => [...prev, assistantMsg]);\n // Completed-message boundary #2 (non-streaming variant): a single request/response\n // call has no intermediate chunks at all, so this is simply \"after the reply\".\n persistHistory([...updatedHistory, assistantMsg]);\n options.onMessage?.(assistantMsg);\n return assistantMsg;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsLoading(false);\n setIsStreaming(false);\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, messages, options, persistHistory],\n );\n\n return {\n messages,\n sendMessage,\n isLoading,\n /** Whether the in-flight `sendMessage` call is a token-level stream (STORA-516). */\n isStreaming,\n error,\n cancel,\n clearMessages,\n setMessages,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AgentOpRecord, AiAgentRunResult, AiChatMessage } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/** One entry in the live run timeline the panel renders while the agent works. */\nexport interface AgentTimelineEntry {\n id: string;\n name: string;\n input: Record<string, unknown>;\n status: 'running' | 'ok' | 'failed';\n summary?: string;\n}\n\nexport interface UseAiAgentOptions extends AiClientOptions {\n onComplete?: (result: AiAgentRunResult) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface RunAgentParams {\n /** The instruction for this turn. Appended to `history` as the last user message. */\n instruction: string;\n document: PageDocument;\n selectedNodeId?: string;\n /** Prior turns of the same conversation, so follow-ups like \"tambahkan juga…\" work. */\n history?: AiChatMessage[];\n stylePreference?: string;\n maxSteps?: number;\n}\n\n/**\n * React binding for agent mode (STORA-530).\n *\n * Owns only the transport and the live run state — it deliberately does NOT apply anything\n * to a document. The resulting `ops` are handed to the caller (the editor's AI panel),\n * which reviews them with the user and then replays them through the editor store, so the\n * whole run lands as a single undoable edit.\n */\nexport function useAiAgent(options: UseAiAgentOptions) {\n const [isRunning, setIsRunning] = useState(false);\n const [step, setStep] = useState(0);\n const [maxSteps, setMaxSteps] = useState(0);\n const [timeline, setTimeline] = useState<AgentTimelineEntry[]>([]);\n const [ops, setOps] = useState<AgentOpRecord[]>([]);\n const [summary, setSummary] = useState<string>('');\n const [result, setResult] = useState<AiAgentRunResult | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const reset = useCallback(() => {\n setTimeline([]);\n setOps([]);\n setSummary('');\n setResult(null);\n setError(null);\n setStep(0);\n setMaxSteps(0);\n }, []);\n\n const abort = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setIsRunning(false);\n }, []);\n\n const run = useCallback(\n async (params: RunAgentParams): Promise<AiAgentRunResult | null> => {\n // A second run while one is in flight would interleave timeline entries from two\n // different conversations — cancel the old one first.\n abortControllerRef.current?.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n reset();\n setIsRunning(true);\n\n const messages: AiChatMessage[] = [\n ...(params.history ?? []),\n { role: 'user', content: params.instruction, timestamp: Date.now() },\n ];\n\n try {\n const runResult = await clientRef.current.runAgent(\n {\n messages,\n document: params.document,\n selectedNodeId: params.selectedNodeId,\n stylePreference: params.stylePreference,\n maxSteps: params.maxSteps,\n },\n {\n onAgentStep: (currentStep, limit) => {\n setStep(currentStep);\n setMaxSteps(limit);\n },\n onToolCall: (call) => {\n setTimeline((prev) => [\n ...prev,\n { id: call.id, name: call.name, input: call.input, status: 'running' },\n ]);\n },\n onToolResult: (toolResult) => {\n setTimeline((prev) =>\n prev.map((entry) =>\n entry.id === toolResult.id\n ? { ...entry, status: toolResult.ok ? 'ok' : 'failed', summary: toolResult.summary }\n : entry,\n ),\n );\n },\n onAgentComplete: (completed) => {\n setOps(completed.ops);\n setSummary(completed.summary);\n setResult(completed);\n optionsRef.current.onComplete?.(completed);\n },\n onError: (err) => {\n setError(err);\n optionsRef.current.onError?.(err);\n },\n },\n { signal: controller.signal },\n );\n\n return runResult;\n } catch (err) {\n const normalized = err instanceof Error ? err : new Error(String(err));\n // An abort is a user action, not a failure to report.\n if (normalized.name !== 'AbortError') {\n setError(normalized);\n optionsRef.current.onError?.(normalized);\n }\n return null;\n } finally {\n setIsRunning(false);\n abortControllerRef.current = null;\n }\n },\n [reset],\n );\n\n return {\n run,\n abort,\n reset,\n isRunning,\n step,\n maxSteps,\n timeline,\n ops,\n summary,\n result,\n error,\n };\n}\n"],"mappings":"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","names":["import_react","import_react"]}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAE9B,cAAc,gBAAgB,CAAC"}
|
package/dist/react/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAiClient
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-T72YWBJS.js";
|
|
4
4
|
|
|
5
5
|
// src/react/use-ai-generator.ts
|
|
6
6
|
import { useState, useCallback, useRef } from "react";
|
|
@@ -329,7 +329,118 @@ function useAiChat(options) {
|
|
|
329
329
|
setMessages
|
|
330
330
|
};
|
|
331
331
|
}
|
|
332
|
+
|
|
333
|
+
// src/react/use-ai-agent.ts
|
|
334
|
+
import { useCallback as useCallback3, useRef as useRef3, useState as useState3 } from "react";
|
|
335
|
+
function useAiAgent(options) {
|
|
336
|
+
const [isRunning, setIsRunning] = useState3(false);
|
|
337
|
+
const [step, setStep] = useState3(0);
|
|
338
|
+
const [maxSteps, setMaxSteps] = useState3(0);
|
|
339
|
+
const [timeline, setTimeline] = useState3([]);
|
|
340
|
+
const [ops, setOps] = useState3([]);
|
|
341
|
+
const [summary, setSummary] = useState3("");
|
|
342
|
+
const [result, setResult] = useState3(null);
|
|
343
|
+
const [error, setError] = useState3(null);
|
|
344
|
+
const abortControllerRef = useRef3(null);
|
|
345
|
+
const clientRef = useRef3(createAiClient(options));
|
|
346
|
+
const optionsRef = useRef3(options);
|
|
347
|
+
optionsRef.current = options;
|
|
348
|
+
const reset = useCallback3(() => {
|
|
349
|
+
setTimeline([]);
|
|
350
|
+
setOps([]);
|
|
351
|
+
setSummary("");
|
|
352
|
+
setResult(null);
|
|
353
|
+
setError(null);
|
|
354
|
+
setStep(0);
|
|
355
|
+
setMaxSteps(0);
|
|
356
|
+
}, []);
|
|
357
|
+
const abort = useCallback3(() => {
|
|
358
|
+
abortControllerRef.current?.abort();
|
|
359
|
+
abortControllerRef.current = null;
|
|
360
|
+
setIsRunning(false);
|
|
361
|
+
}, []);
|
|
362
|
+
const run = useCallback3(
|
|
363
|
+
async (params) => {
|
|
364
|
+
abortControllerRef.current?.abort();
|
|
365
|
+
const controller = new AbortController();
|
|
366
|
+
abortControllerRef.current = controller;
|
|
367
|
+
reset();
|
|
368
|
+
setIsRunning(true);
|
|
369
|
+
const messages = [
|
|
370
|
+
...params.history ?? [],
|
|
371
|
+
{ role: "user", content: params.instruction, timestamp: Date.now() }
|
|
372
|
+
];
|
|
373
|
+
try {
|
|
374
|
+
const runResult = await clientRef.current.runAgent(
|
|
375
|
+
{
|
|
376
|
+
messages,
|
|
377
|
+
document: params.document,
|
|
378
|
+
selectedNodeId: params.selectedNodeId,
|
|
379
|
+
stylePreference: params.stylePreference,
|
|
380
|
+
maxSteps: params.maxSteps
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
onAgentStep: (currentStep, limit) => {
|
|
384
|
+
setStep(currentStep);
|
|
385
|
+
setMaxSteps(limit);
|
|
386
|
+
},
|
|
387
|
+
onToolCall: (call) => {
|
|
388
|
+
setTimeline((prev) => [
|
|
389
|
+
...prev,
|
|
390
|
+
{ id: call.id, name: call.name, input: call.input, status: "running" }
|
|
391
|
+
]);
|
|
392
|
+
},
|
|
393
|
+
onToolResult: (toolResult) => {
|
|
394
|
+
setTimeline(
|
|
395
|
+
(prev) => prev.map(
|
|
396
|
+
(entry) => entry.id === toolResult.id ? { ...entry, status: toolResult.ok ? "ok" : "failed", summary: toolResult.summary } : entry
|
|
397
|
+
)
|
|
398
|
+
);
|
|
399
|
+
},
|
|
400
|
+
onAgentComplete: (completed) => {
|
|
401
|
+
setOps(completed.ops);
|
|
402
|
+
setSummary(completed.summary);
|
|
403
|
+
setResult(completed);
|
|
404
|
+
optionsRef.current.onComplete?.(completed);
|
|
405
|
+
},
|
|
406
|
+
onError: (err) => {
|
|
407
|
+
setError(err);
|
|
408
|
+
optionsRef.current.onError?.(err);
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
{ signal: controller.signal }
|
|
412
|
+
);
|
|
413
|
+
return runResult;
|
|
414
|
+
} catch (err) {
|
|
415
|
+
const normalized = err instanceof Error ? err : new Error(String(err));
|
|
416
|
+
if (normalized.name !== "AbortError") {
|
|
417
|
+
setError(normalized);
|
|
418
|
+
optionsRef.current.onError?.(normalized);
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
} finally {
|
|
422
|
+
setIsRunning(false);
|
|
423
|
+
abortControllerRef.current = null;
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
[reset]
|
|
427
|
+
);
|
|
428
|
+
return {
|
|
429
|
+
run,
|
|
430
|
+
abort,
|
|
431
|
+
reset,
|
|
432
|
+
isRunning,
|
|
433
|
+
step,
|
|
434
|
+
maxSteps,
|
|
435
|
+
timeline,
|
|
436
|
+
ops,
|
|
437
|
+
summary,
|
|
438
|
+
result,
|
|
439
|
+
error
|
|
440
|
+
};
|
|
441
|
+
}
|
|
332
442
|
export {
|
|
443
|
+
useAiAgent,
|
|
333
444
|
useAiChat,
|
|
334
445
|
useAiGenerator
|
|
335
446
|
};
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/use-ai-generator.ts","../../src/react/use-ai-chat.ts"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiStreamCallbacks,\n} from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\nexport interface UseAiGeneratorOptions extends AiClientOptions {\n onSuccess?: (data: PageDocument | Node) => void;\n onError?: (error: Error) => void;\n}\n\nexport function useAiGenerator(options: UseAiGeneratorOptions) {\n const [isGenerating, setIsGenerating] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [currentStep, setCurrentStep] = useState<string>('');\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n\n // Cancel any in-flight request\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n }\n }, []);\n\n /**\n * Standard full page generator (non-streaming).\n */\n const generatePage = useCallback(\n async (params: AiGeneratePageRequest): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating full page...');\n\n try {\n const doc = await clientRef.current.generatePage(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(doc);\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each step (onStatus, onMetadata, onSection, onComplete).\n */\n const streamPage = useCallback(\n async (\n params: AiGeneratePageRequest,\n streamCallbacks?: AiStreamCallbacks,\n ): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(true);\n setError(null);\n setCurrentStep('Connecting to AI stream...');\n\n try {\n const doc = await clientRef.current.streamPage(\n params,\n {\n onStatus: (msg) => {\n setCurrentStep(msg);\n streamCallbacks?.onStatus?.(msg);\n },\n onMetadata: (metadata, rootPage) => {\n streamCallbacks?.onMetadata?.(metadata, rootPage);\n },\n onSection: (section, index, total) => {\n setCurrentStep(`Section ${index + 1}/${total} rendered`);\n streamCallbacks?.onSection?.(section, index, total);\n },\n onComplete: (completedDoc) => {\n setCurrentStep('Completed');\n streamCallbacks?.onComplete?.(completedDoc);\n options.onSuccess?.(completedDoc);\n },\n onError: (err) => {\n setError(err);\n streamCallbacks?.onError?.(err);\n options.onError?.(err);\n },\n },\n { signal: ac.signal },\n );\n\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const generateSection = useCallback(\n async (params: AiGenerateSectionRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating section...');\n\n try {\n const node = await clientRef.current.generateSection(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const refactorNode = useCallback(\n async (params: AiRefactorNodeRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Refactoring node...');\n\n try {\n const node = await clientRef.current.refactorNode(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n return {\n generatePage,\n streamPage,\n generateSection,\n refactorNode,\n isGenerating,\n isStreaming,\n currentStep,\n error,\n cancel,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AiChatMessage, AiChatResponse } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/**\n * Optional, host-implemented chat history persistence (STORA-520). `@kubuild/ai` stays\n * storage-agnostic — it never imports `localStorage` or any concrete storage mechanism\n * itself; the host supplies an adapter backed by whatever it wants (browser\n * `localStorage`, IndexedDB, a Stora.page backend call, etc.).\n */\nexport interface AiChatHistoryStorageAdapter {\n /** Called once on mount to seed `messages` before the user sends anything. */\n loadHistory(): Promise<AiChatMessage[]> | AiChatMessage[];\n /**\n * Called at completed-message boundaries (a user message just sent, or an assistant\n * reply that just finished/streamed to completion) — never on every mid-stream token,\n * see `useAiChat`'s persistence notes.\n */\n saveHistory(messages: AiChatMessage[]): Promise<void> | void;\n}\n\nexport interface UseAiChatOptions extends AiClientOptions {\n initialMessages?: AiChatMessage[];\n onMessage?: (message: AiChatMessage) => void;\n onError?: (error: Error) => void;\n /**\n * Fired for every partial token/chunk of a streaming chat response (STORA-516),\n * mirroring `useAiGenerator`'s `AiStreamCallbacks` naming convention. Optional — the\n * `messages` array already updates incrementally on its own, this is only for hosts\n * that want a side-channel (e.g. custom rendering, telemetry) into the raw chunks.\n */\n onChunk?: (delta: string, content: string) => void;\n /**\n * Optional storage adapter (STORA-520) used to persist/restore chat history across\n * reloads. Without it, behavior is exactly as before: pure in-memory React state, lost\n * on reload/unmount. `loadHistory()` is called once on mount to hydrate `messages`\n * (only overriding `initialMessages` when it resolves a non-empty array);\n * `saveHistory()` is called at completed-message boundaries — after a user message is\n * appended, and after an assistant reply finishes (streamed or not) — intentionally\n * *not* on every streaming chunk, to avoid hammering the adapter mid-stream.\n */\n historyStorage?: AiChatHistoryStorageAdapter;\n}\n\nexport interface SendMessageOptions {\n currentDocument?: PageDocument;\n selectedNodeId?: string;\n systemPrompt?: string;\n /**\n * Opt out of token-level streaming for this call (STORA-515/516 default to `true`).\n * When `false`, behaves exactly like the pre-streaming single request/response call.\n */\n stream?: boolean;\n}\n\nexport function useAiChat(options: UseAiChatOptions) {\n const [messages, setMessages] = useState<AiChatMessage[]>(options.initialMessages || []);\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const historyStorageRef = useRef(options.historyStorage);\n historyStorageRef.current = options.historyStorage;\n\n /**\n * STORA-520 — best-effort persistence: a storage failure (quota exceeded, network\n * error on a remote adapter, etc.) must never break the chat itself, so errors are\n * swallowed here rather than surfaced through `setError`/`onError` (those are reserved\n * for the actual chat request failing).\n */\n const persistHistory = useCallback((history: AiChatMessage[]) => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n try {\n void Promise.resolve(adapter.saveHistory(history)).catch(() => undefined);\n } catch {\n // Synchronous throw from a non-Promise-returning adapter — ignore, same reasoning.\n }\n }, []);\n\n // STORA-520 — hydrate from storage once on mount. Only overrides `initialMessages`\n // when the adapter actually resolves a non-empty history, so a fresh/empty adapter\n // (e.g. first-ever visit) doesn't clobber a host-provided `initialMessages` seed.\n useEffect(() => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n let cancelled = false;\n Promise.resolve(adapter.loadHistory())\n .then((loaded) => {\n if (!cancelled && Array.isArray(loaded) && loaded.length > 0) {\n setMessages(loaded);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const e = err instanceof Error ? err : new Error(String(err));\n options.onError?.(e);\n });\n return () => {\n cancelled = true;\n };\n // Deliberately empty deps: `historyStorage`/`onError` are read via refs/closures at\n // call time and this must run exactly once per mount, not whenever the caller passes\n // a fresh options object.\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n // Aborts the underlying fetch (and, for a streaming request, the in-flight\n // ReadableStream read) — not just local UI state (STORA-516 AC).\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n }\n }, []);\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n persistHistory([]);\n }, [persistHistory]);\n\n const sendMessage = useCallback(\n async (content: string, sendOptions?: SendMessageOptions): Promise<AiChatMessage | null> => {\n if (!content.trim()) return null;\n\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n const userMessage: AiChatMessage = {\n role: 'user',\n content: content.trim(),\n timestamp: Date.now(),\n };\n\n const updatedHistory = [...messages, userMessage];\n setMessages(updatedHistory);\n setIsLoading(true);\n setError(null);\n // Completed-message boundary #1 (STORA-520): the user's message is final the\n // instant it's sent — persist it now rather than waiting for the assistant reply,\n // so a reload mid-request doesn't lose it.\n persistHistory(updatedHistory);\n\n const useStream = sendOptions?.stream !== false;\n\n try {\n if (useStream) {\n setIsStreaming(true);\n\n // Placeholder assistant message, filled in incrementally as chunks arrive.\n let assistantIndex = -1;\n setMessages((prev) => {\n assistantIndex = prev.length;\n return [...prev, { role: 'assistant', content: '', timestamp: Date.now() }];\n });\n\n const finalMessage = await clientRef.current.chatStream(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n {\n onChatChunk: (delta, contentSoFar) => {\n options.onChunk?.(delta, contentSoFar);\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = { ...next[assistantIndex], content: contentSoFar };\n return next;\n });\n },\n onChatComplete: (message) => {\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = message;\n return next;\n });\n },\n },\n { signal: ac.signal },\n );\n\n // Completed-message boundary #2 (STORA-520): the stream finished — persist the\n // fully assembled assistant message, never the intermediate per-chunk content\n // (`onChatChunk` above deliberately does not call `persistHistory`).\n persistHistory([...updatedHistory, finalMessage]);\n options.onMessage?.(finalMessage);\n return finalMessage;\n }\n\n const response: AiChatResponse = await clientRef.current.chat(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n { signal: ac.signal },\n );\n\n const assistantMsg = response.message;\n setMessages((prev) => [...prev, assistantMsg]);\n // Completed-message boundary #2 (non-streaming variant): a single request/response\n // call has no intermediate chunks at all, so this is simply \"after the reply\".\n persistHistory([...updatedHistory, assistantMsg]);\n options.onMessage?.(assistantMsg);\n return assistantMsg;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsLoading(false);\n setIsStreaming(false);\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, messages, options, persistHistory],\n );\n\n return {\n messages,\n sendMessage,\n isLoading,\n /** Whether the in-flight `sendMessage` call is a token-level stream (STORA-516). */\n isStreaming,\n error,\n cancel,\n clearMessages,\n setMessages,\n };\n}\n"],"mappings":"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","names":["useState","useCallback","useRef","useState","useRef","useCallback"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/use-ai-generator.ts","../../src/react/use-ai-chat.ts","../../src/react/use-ai-agent.ts"],"sourcesContent":["import { useState, useCallback, useRef } from 'react';\nimport type { PageDocument, Node } from '@kubuild/schema';\nimport type {\n AiGeneratePageRequest,\n AiGenerateSectionRequest,\n AiRefactorNodeRequest,\n AiStreamCallbacks,\n} from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\nexport interface UseAiGeneratorOptions extends AiClientOptions {\n onSuccess?: (data: PageDocument | Node) => void;\n onError?: (error: Error) => void;\n}\n\nexport function useAiGenerator(options: UseAiGeneratorOptions) {\n const [isGenerating, setIsGenerating] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [currentStep, setCurrentStep] = useState<string>('');\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n\n // Cancel any in-flight request\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n }\n }, []);\n\n /**\n * Standard full page generator (non-streaming).\n */\n const generatePage = useCallback(\n async (params: AiGeneratePageRequest): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating full page...');\n\n try {\n const doc = await clientRef.current.generatePage(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(doc);\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n /**\n * Progressive section streamer (SSE).\n * Calls callbacks on each step (onStatus, onMetadata, onSection, onComplete).\n */\n const streamPage = useCallback(\n async (\n params: AiGeneratePageRequest,\n streamCallbacks?: AiStreamCallbacks,\n ): Promise<PageDocument | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(true);\n setError(null);\n setCurrentStep('Connecting to AI stream...');\n\n try {\n const doc = await clientRef.current.streamPage(\n params,\n {\n onStatus: (msg) => {\n setCurrentStep(msg);\n streamCallbacks?.onStatus?.(msg);\n },\n onMetadata: (metadata, rootPage) => {\n streamCallbacks?.onMetadata?.(metadata, rootPage);\n },\n onSection: (section, index, total) => {\n setCurrentStep(`Section ${index + 1}/${total} rendered`);\n streamCallbacks?.onSection?.(section, index, total);\n },\n onComplete: (completedDoc) => {\n setCurrentStep('Completed');\n streamCallbacks?.onComplete?.(completedDoc);\n options.onSuccess?.(completedDoc);\n },\n onError: (err) => {\n setError(err);\n streamCallbacks?.onError?.(err);\n options.onError?.(err);\n },\n },\n { signal: ac.signal },\n );\n\n return doc;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setIsStreaming(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const generateSection = useCallback(\n async (params: AiGenerateSectionRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Generating section...');\n\n try {\n const node = await clientRef.current.generateSection(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n const refactorNode = useCallback(\n async (params: AiRefactorNodeRequest): Promise<Node | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Refactoring node...');\n\n try {\n const node = await clientRef.current.refactorNode(params, {\n signal: ac.signal,\n });\n options.onSuccess?.(node);\n return node;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsGenerating(false);\n setCurrentStep('');\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, options],\n );\n\n return {\n generatePage,\n streamPage,\n generateSection,\n refactorNode,\n isGenerating,\n isStreaming,\n currentStep,\n error,\n cancel,\n };\n}\n","import { useState, useCallback, useRef, useEffect } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AiChatMessage, AiChatResponse } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/**\n * Optional, host-implemented chat history persistence (STORA-520). `@kubuild/ai` stays\n * storage-agnostic — it never imports `localStorage` or any concrete storage mechanism\n * itself; the host supplies an adapter backed by whatever it wants (browser\n * `localStorage`, IndexedDB, a Stora.page backend call, etc.).\n */\nexport interface AiChatHistoryStorageAdapter {\n /** Called once on mount to seed `messages` before the user sends anything. */\n loadHistory(): Promise<AiChatMessage[]> | AiChatMessage[];\n /**\n * Called at completed-message boundaries (a user message just sent, or an assistant\n * reply that just finished/streamed to completion) — never on every mid-stream token,\n * see `useAiChat`'s persistence notes.\n */\n saveHistory(messages: AiChatMessage[]): Promise<void> | void;\n}\n\nexport interface UseAiChatOptions extends AiClientOptions {\n initialMessages?: AiChatMessage[];\n onMessage?: (message: AiChatMessage) => void;\n onError?: (error: Error) => void;\n /**\n * Fired for every partial token/chunk of a streaming chat response (STORA-516),\n * mirroring `useAiGenerator`'s `AiStreamCallbacks` naming convention. Optional — the\n * `messages` array already updates incrementally on its own, this is only for hosts\n * that want a side-channel (e.g. custom rendering, telemetry) into the raw chunks.\n */\n onChunk?: (delta: string, content: string) => void;\n /**\n * Optional storage adapter (STORA-520) used to persist/restore chat history across\n * reloads. Without it, behavior is exactly as before: pure in-memory React state, lost\n * on reload/unmount. `loadHistory()` is called once on mount to hydrate `messages`\n * (only overriding `initialMessages` when it resolves a non-empty array);\n * `saveHistory()` is called at completed-message boundaries — after a user message is\n * appended, and after an assistant reply finishes (streamed or not) — intentionally\n * *not* on every streaming chunk, to avoid hammering the adapter mid-stream.\n */\n historyStorage?: AiChatHistoryStorageAdapter;\n}\n\nexport interface SendMessageOptions {\n currentDocument?: PageDocument;\n selectedNodeId?: string;\n systemPrompt?: string;\n /**\n * Opt out of token-level streaming for this call (STORA-515/516 default to `true`).\n * When `false`, behaves exactly like the pre-streaming single request/response call.\n */\n stream?: boolean;\n}\n\nexport function useAiChat(options: UseAiChatOptions) {\n const [messages, setMessages] = useState<AiChatMessage[]>(options.initialMessages || []);\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const historyStorageRef = useRef(options.historyStorage);\n historyStorageRef.current = options.historyStorage;\n\n /**\n * STORA-520 — best-effort persistence: a storage failure (quota exceeded, network\n * error on a remote adapter, etc.) must never break the chat itself, so errors are\n * swallowed here rather than surfaced through `setError`/`onError` (those are reserved\n * for the actual chat request failing).\n */\n const persistHistory = useCallback((history: AiChatMessage[]) => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n try {\n void Promise.resolve(adapter.saveHistory(history)).catch(() => undefined);\n } catch {\n // Synchronous throw from a non-Promise-returning adapter — ignore, same reasoning.\n }\n }, []);\n\n // STORA-520 — hydrate from storage once on mount. Only overrides `initialMessages`\n // when the adapter actually resolves a non-empty history, so a fresh/empty adapter\n // (e.g. first-ever visit) doesn't clobber a host-provided `initialMessages` seed.\n useEffect(() => {\n const adapter = historyStorageRef.current;\n if (!adapter) return;\n let cancelled = false;\n Promise.resolve(adapter.loadHistory())\n .then((loaded) => {\n if (!cancelled && Array.isArray(loaded) && loaded.length > 0) {\n setMessages(loaded);\n }\n })\n .catch((err: unknown) => {\n if (cancelled) return;\n const e = err instanceof Error ? err : new Error(String(err));\n options.onError?.(e);\n });\n return () => {\n cancelled = true;\n };\n // Deliberately empty deps: `historyStorage`/`onError` are read via refs/closures at\n // call time and this must run exactly once per mount, not whenever the caller passes\n // a fresh options object.\n }, []);\n\n const cancel = useCallback(() => {\n if (abortControllerRef.current) {\n // Aborts the underlying fetch (and, for a streaming request, the in-flight\n // ReadableStream read) — not just local UI state (STORA-516 AC).\n abortControllerRef.current.abort();\n abortControllerRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n }\n }, []);\n\n const clearMessages = useCallback(() => {\n setMessages([]);\n setError(null);\n persistHistory([]);\n }, [persistHistory]);\n\n const sendMessage = useCallback(\n async (content: string, sendOptions?: SendMessageOptions): Promise<AiChatMessage | null> => {\n if (!content.trim()) return null;\n\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n const userMessage: AiChatMessage = {\n role: 'user',\n content: content.trim(),\n timestamp: Date.now(),\n };\n\n const updatedHistory = [...messages, userMessage];\n setMessages(updatedHistory);\n setIsLoading(true);\n setError(null);\n // Completed-message boundary #1 (STORA-520): the user's message is final the\n // instant it's sent — persist it now rather than waiting for the assistant reply,\n // so a reload mid-request doesn't lose it.\n persistHistory(updatedHistory);\n\n const useStream = sendOptions?.stream !== false;\n\n try {\n if (useStream) {\n setIsStreaming(true);\n\n // Placeholder assistant message, filled in incrementally as chunks arrive.\n let assistantIndex = -1;\n setMessages((prev) => {\n assistantIndex = prev.length;\n return [...prev, { role: 'assistant', content: '', timestamp: Date.now() }];\n });\n\n const finalMessage = await clientRef.current.chatStream(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n {\n onChatChunk: (delta, contentSoFar) => {\n options.onChunk?.(delta, contentSoFar);\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = { ...next[assistantIndex], content: contentSoFar };\n return next;\n });\n },\n onChatComplete: (message) => {\n setMessages((prev) => {\n if (assistantIndex < 0 || assistantIndex >= prev.length) return prev;\n const next = prev.slice();\n next[assistantIndex] = message;\n return next;\n });\n },\n },\n { signal: ac.signal },\n );\n\n // Completed-message boundary #2 (STORA-520): the stream finished — persist the\n // fully assembled assistant message, never the intermediate per-chunk content\n // (`onChatChunk` above deliberately does not call `persistHistory`).\n persistHistory([...updatedHistory, finalMessage]);\n options.onMessage?.(finalMessage);\n return finalMessage;\n }\n\n const response: AiChatResponse = await clientRef.current.chat(\n {\n messages: updatedHistory,\n currentDocument: sendOptions?.currentDocument,\n selectedNodeId: sendOptions?.selectedNodeId,\n systemPrompt: sendOptions?.systemPrompt,\n },\n { signal: ac.signal },\n );\n\n const assistantMsg = response.message;\n setMessages((prev) => [...prev, assistantMsg]);\n // Completed-message boundary #2 (non-streaming variant): a single request/response\n // call has no intermediate chunks at all, so this is simply \"after the reply\".\n persistHistory([...updatedHistory, assistantMsg]);\n options.onMessage?.(assistantMsg);\n return assistantMsg;\n } catch (err: unknown) {\n if (ac.signal.aborted) return null;\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n return null;\n } finally {\n if (abortControllerRef.current === ac) {\n setIsLoading(false);\n setIsStreaming(false);\n abortControllerRef.current = null;\n }\n }\n },\n [cancel, messages, options, persistHistory],\n );\n\n return {\n messages,\n sendMessage,\n isLoading,\n /** Whether the in-flight `sendMessage` call is a token-level stream (STORA-516). */\n isStreaming,\n error,\n cancel,\n clearMessages,\n setMessages,\n };\n}\n","import { useCallback, useRef, useState } from 'react';\nimport type { PageDocument } from '@kubuild/schema';\nimport type { AgentOpRecord, AiAgentRunResult, AiChatMessage } from '../types';\nimport { createAiClient, type AiClientOptions, KubuildAiClient } from '../client/ai-client';\n\n/** One entry in the live run timeline the panel renders while the agent works. */\nexport interface AgentTimelineEntry {\n id: string;\n name: string;\n input: Record<string, unknown>;\n status: 'running' | 'ok' | 'failed';\n summary?: string;\n}\n\nexport interface UseAiAgentOptions extends AiClientOptions {\n onComplete?: (result: AiAgentRunResult) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface RunAgentParams {\n /** The instruction for this turn. Appended to `history` as the last user message. */\n instruction: string;\n document: PageDocument;\n selectedNodeId?: string;\n /** Prior turns of the same conversation, so follow-ups like \"tambahkan juga…\" work. */\n history?: AiChatMessage[];\n stylePreference?: string;\n maxSteps?: number;\n}\n\n/**\n * React binding for agent mode (STORA-530).\n *\n * Owns only the transport and the live run state — it deliberately does NOT apply anything\n * to a document. The resulting `ops` are handed to the caller (the editor's AI panel),\n * which reviews them with the user and then replays them through the editor store, so the\n * whole run lands as a single undoable edit.\n */\nexport function useAiAgent(options: UseAiAgentOptions) {\n const [isRunning, setIsRunning] = useState(false);\n const [step, setStep] = useState(0);\n const [maxSteps, setMaxSteps] = useState(0);\n const [timeline, setTimeline] = useState<AgentTimelineEntry[]>([]);\n const [ops, setOps] = useState<AgentOpRecord[]>([]);\n const [summary, setSummary] = useState<string>('');\n const [result, setResult] = useState<AiAgentRunResult | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const abortControllerRef = useRef<AbortController | null>(null);\n const clientRef = useRef<KubuildAiClient>(createAiClient(options));\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const reset = useCallback(() => {\n setTimeline([]);\n setOps([]);\n setSummary('');\n setResult(null);\n setError(null);\n setStep(0);\n setMaxSteps(0);\n }, []);\n\n const abort = useCallback(() => {\n abortControllerRef.current?.abort();\n abortControllerRef.current = null;\n setIsRunning(false);\n }, []);\n\n const run = useCallback(\n async (params: RunAgentParams): Promise<AiAgentRunResult | null> => {\n // A second run while one is in flight would interleave timeline entries from two\n // different conversations — cancel the old one first.\n abortControllerRef.current?.abort();\n const controller = new AbortController();\n abortControllerRef.current = controller;\n\n reset();\n setIsRunning(true);\n\n const messages: AiChatMessage[] = [\n ...(params.history ?? []),\n { role: 'user', content: params.instruction, timestamp: Date.now() },\n ];\n\n try {\n const runResult = await clientRef.current.runAgent(\n {\n messages,\n document: params.document,\n selectedNodeId: params.selectedNodeId,\n stylePreference: params.stylePreference,\n maxSteps: params.maxSteps,\n },\n {\n onAgentStep: (currentStep, limit) => {\n setStep(currentStep);\n setMaxSteps(limit);\n },\n onToolCall: (call) => {\n setTimeline((prev) => [\n ...prev,\n { id: call.id, name: call.name, input: call.input, status: 'running' },\n ]);\n },\n onToolResult: (toolResult) => {\n setTimeline((prev) =>\n prev.map((entry) =>\n entry.id === toolResult.id\n ? { ...entry, status: toolResult.ok ? 'ok' : 'failed', summary: toolResult.summary }\n : entry,\n ),\n );\n },\n onAgentComplete: (completed) => {\n setOps(completed.ops);\n setSummary(completed.summary);\n setResult(completed);\n optionsRef.current.onComplete?.(completed);\n },\n onError: (err) => {\n setError(err);\n optionsRef.current.onError?.(err);\n },\n },\n { signal: controller.signal },\n );\n\n return runResult;\n } catch (err) {\n const normalized = err instanceof Error ? err : new Error(String(err));\n // An abort is a user action, not a failure to report.\n if (normalized.name !== 'AbortError') {\n setError(normalized);\n optionsRef.current.onError?.(normalized);\n }\n return null;\n } finally {\n setIsRunning(false);\n abortControllerRef.current = null;\n }\n },\n [reset],\n );\n\n return {\n run,\n abort,\n reset,\n isRunning,\n step,\n maxSteps,\n timeline,\n ops,\n summary,\n result,\n error,\n };\n}\n"],"mappings":"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","names":["useState","useCallback","useRef","useState","useRef","useCallback","useCallback","useRef","useState","useState","useRef","useCallback"]}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PageDocument } from '@kubuild/schema';
|
|
2
|
+
import type { AgentOpRecord, AiAgentRunResult, AiChatMessage } from '../types';
|
|
3
|
+
import { type AiClientOptions } from '../client/ai-client';
|
|
4
|
+
/** One entry in the live run timeline the panel renders while the agent works. */
|
|
5
|
+
export interface AgentTimelineEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
input: Record<string, unknown>;
|
|
9
|
+
status: 'running' | 'ok' | 'failed';
|
|
10
|
+
summary?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface UseAiAgentOptions extends AiClientOptions {
|
|
13
|
+
onComplete?: (result: AiAgentRunResult) => void;
|
|
14
|
+
onError?: (error: Error) => void;
|
|
15
|
+
}
|
|
16
|
+
export interface RunAgentParams {
|
|
17
|
+
/** The instruction for this turn. Appended to `history` as the last user message. */
|
|
18
|
+
instruction: string;
|
|
19
|
+
document: PageDocument;
|
|
20
|
+
selectedNodeId?: string;
|
|
21
|
+
/** Prior turns of the same conversation, so follow-ups like "tambahkan juga…" work. */
|
|
22
|
+
history?: AiChatMessage[];
|
|
23
|
+
stylePreference?: string;
|
|
24
|
+
maxSteps?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* React binding for agent mode (STORA-530).
|
|
28
|
+
*
|
|
29
|
+
* Owns only the transport and the live run state — it deliberately does NOT apply anything
|
|
30
|
+
* to a document. The resulting `ops` are handed to the caller (the editor's AI panel),
|
|
31
|
+
* which reviews them with the user and then replays them through the editor store, so the
|
|
32
|
+
* whole run lands as a single undoable edit.
|
|
33
|
+
*/
|
|
34
|
+
export declare function useAiAgent(options: UseAiAgentOptions): {
|
|
35
|
+
run: (params: RunAgentParams) => Promise<AiAgentRunResult | null>;
|
|
36
|
+
abort: () => void;
|
|
37
|
+
reset: () => void;
|
|
38
|
+
isRunning: boolean;
|
|
39
|
+
step: number;
|
|
40
|
+
maxSteps: number;
|
|
41
|
+
timeline: AgentTimelineEntry[];
|
|
42
|
+
ops: AgentOpRecord[];
|
|
43
|
+
summary: string;
|
|
44
|
+
result: AiAgentRunResult | null;
|
|
45
|
+
error: Error | null;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=use-ai-agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-ai-agent.d.ts","sourceRoot":"","sources":["../../src/react/use-ai-agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,EAAkB,KAAK,eAAe,EAAmB,MAAM,qBAAqB,CAAC;AAE5F,kFAAkF;AAClF,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,SAAS,GAAG,IAAI,GAAG,QAAQ,CAAC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,qFAAqF;IACrF,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,YAAY,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB;kBAgClC,cAAc,KAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;;;;;;;;;;;EAwFnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,qBAAa,gBAAiB,YAAW,iBAAiB;IACxD,QAAQ,CAAC,IAAI,eAAe;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,aAAa,CAAC,CAAyB;gBAEnC,OAAO,EAAE,uBAAuB;IAa5C,OAAO,CAAC,aAAa;IAgBf,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IA4DnF;;;;OAIG;IACI,cAAc,CACnB,MAAM,EAAE,wBAAwB,GAC/B,cAAc,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,CAAC;CA6E1D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"custom.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/custom.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,wBAAwB;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,wBAAwB,KAAK,OAAO,CAAC;IAClE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3C;AAED,qBAAa,iBAAkB,YAAW,iBAAiB;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,OAAO,CAAC,CAAyB;IACzC,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,aAAa,CAA4B;gBAErC,OAAO,EAAE,wBAAwB;IA8CvC,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;CAyBpF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../../src/server/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACzB,MAAM,aAAa,CAAC;AAIrB,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,aAAc,YAAW,iBAAiB;IACrD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,oBAAoB;IAWzC,OAAO,CAAC,aAAa;IAiBf,QAAQ,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,wBAAwB,CAAC;IAkEnF;;OAEG;IACI,cAAc,CACnB,MAAM,EAAE,wBAAwB,GAC/B,cAAc,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,CAAC;CAmE1D"}
|