@kubuild/ai 0.4.0 → 0.6.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createAiClient
3
- } from "../chunk-T72YWBJS.js";
3
+ } from "../chunk-K7CBTQCY.js";
4
4
 
5
5
  // src/react/use-ai-generator.ts
6
6
  import { useState, useCallback, useRef } from "react";
@@ -168,8 +168,38 @@ function useAiGenerator(options) {
168
168
  },
169
169
  [cancel, options]
170
170
  );
171
+ const planPage = useCallback(
172
+ async (params) => {
173
+ cancel();
174
+ const ac = new AbortController();
175
+ abortControllerRef.current = ac;
176
+ setIsGenerating(true);
177
+ setIsStreaming(false);
178
+ setError(null);
179
+ setCurrentStep("Planning page structure...");
180
+ try {
181
+ const plan = await clientRef.current.planPage(params, {
182
+ signal: ac.signal
183
+ });
184
+ return plan;
185
+ } catch (err) {
186
+ const e = err instanceof Error ? err : new Error(String(err));
187
+ setError(e);
188
+ options.onError?.(e);
189
+ throw e;
190
+ } finally {
191
+ if (abortControllerRef.current === ac) {
192
+ setIsGenerating(false);
193
+ setCurrentStep("");
194
+ abortControllerRef.current = null;
195
+ }
196
+ }
197
+ },
198
+ [cancel, options]
199
+ );
171
200
  return {
172
201
  generatePage,
202
+ planPage,
173
203
  streamPage,
174
204
  generateSection,
175
205
  refactorNode,
@@ -1 +1 @@
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":";;;;;AAAA,SAAS,UAAU,aAAa,cAAc;AAevC,SAAS,eAAe,SAAgC;AAC7D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,KAAK;AACpD,QAAM,CAAC,aAAa,cAAc,IAAI,SAAiB,EAAE;AACzD,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AAErD,QAAM,qBAAqB,OAA+B,IAAI;AAC9D,QAAM,YAAY,OAAwB,eAAe,OAAO,CAAC;AAGjE,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,mBAAmB,SAAS;AAC9B,yBAAmB,QAAQ,MAAM;AACjC,yBAAmB,UAAU;AAC7B,sBAAgB,KAAK;AACrB,qBAAe,KAAK;AACpB,qBAAe,EAAE;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,CAAC;AAKL,QAAM,eAAe;AAAA,IACnB,OAAO,WAAgE;AACrE,aAAO;AACP,YAAM,KAAK,IAAI,gBAAgB;AAC/B,yBAAmB,UAAU;AAE7B,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,eAAS,IAAI;AACb,qBAAe,yBAAyB;AAExC,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,QAAQ,aAAa,QAAQ;AAAA,UACvD,QAAQ,GAAG;AAAA,QACb,CAAC;AACD,gBAAQ,YAAY,GAAG;AACvB,eAAO;AAAA,MACT,SAAS,KAAc;AACrB,YAAI,GAAG,OAAO,QAAS,QAAO;AAC9B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,iBAAS,CAAC;AACV,gBAAQ,UAAU,CAAC;AACnB,eAAO;AAAA,MACT,UAAE;AACA,YAAI,mBAAmB,YAAY,IAAI;AACrC,0BAAgB,KAAK;AACrB,yBAAe,EAAE;AACjB,6BAAmB,UAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EAClB;AAMA,QAAM,aAAa;AAAA,IACjB,OACE,QACA,oBACiC;AACjC,aAAO;AACP,YAAM,KAAK,IAAI,gBAAgB;AAC/B,yBAAmB,UAAU;AAE7B,sBAAgB,IAAI;AACpB,qBAAe,IAAI;AACnB,eAAS,IAAI;AACb,qBAAe,4BAA4B;AAE3C,UAAI;AACF,cAAM,MAAM,MAAM,UAAU,QAAQ;AAAA,UAClC;AAAA,UACA;AAAA,YACE,UAAU,CAAC,QAAQ;AACjB,6BAAe,GAAG;AAClB,+BAAiB,WAAW,GAAG;AAAA,YACjC;AAAA,YACA,YAAY,CAAC,UAAU,aAAa;AAClC,+BAAiB,aAAa,UAAU,QAAQ;AAAA,YAClD;AAAA,YACA,WAAW,CAAC,SAAS,OAAO,UAAU;AACpC,6BAAe,WAAW,QAAQ,CAAC,IAAI,KAAK,WAAW;AACvD,+BAAiB,YAAY,SAAS,OAAO,KAAK;AAAA,YACpD;AAAA,YACA,YAAY,CAAC,iBAAiB;AAC5B,6BAAe,WAAW;AAC1B,+BAAiB,aAAa,YAAY;AAC1C,sBAAQ,YAAY,YAAY;AAAA,YAClC;AAAA,YACA,SAAS,CAAC,QAAQ;AAChB,uBAAS,GAAG;AACZ,+BAAiB,UAAU,GAAG;AAC9B,sBAAQ,UAAU,GAAG;AAAA,YACvB;AAAA,UACF;AAAA,UACA,EAAE,QAAQ,GAAG,OAAO;AAAA,QACtB;AAEA,eAAO;AAAA,MACT,SAAS,KAAc;AACrB,YAAI,GAAG,OAAO,QAAS,QAAO;AAC9B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,iBAAS,CAAC;AACV,gBAAQ,UAAU,CAAC;AACnB,eAAO;AAAA,MACT,UAAE;AACA,YAAI,mBAAmB,YAAY,IAAI;AACrC,0BAAgB,KAAK;AACrB,yBAAe,KAAK;AACpB,yBAAe,EAAE;AACjB,6BAAmB,UAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EAClB;AAEA,QAAM,kBAAkB;AAAA,IACtB,OAAO,WAA2D;AAChE,aAAO;AACP,YAAM,KAAK,IAAI,gBAAgB;AAC/B,yBAAmB,UAAU;AAE7B,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,eAAS,IAAI;AACb,qBAAe,uBAAuB;AAEtC,UAAI;AACF,cAAM,OAAO,MAAM,UAAU,QAAQ,gBAAgB,QAAQ;AAAA,UAC3D,QAAQ,GAAG;AAAA,QACb,CAAC;AACD,gBAAQ,YAAY,IAAI;AACxB,eAAO;AAAA,MACT,SAAS,KAAc;AACrB,YAAI,GAAG,OAAO,QAAS,QAAO;AAC9B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,iBAAS,CAAC;AACV,gBAAQ,UAAU,CAAC;AACnB,eAAO;AAAA,MACT,UAAE;AACA,YAAI,mBAAmB,YAAY,IAAI;AACrC,0BAAgB,KAAK;AACrB,yBAAe,EAAE;AACjB,6BAAmB,UAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EAClB;AAEA,QAAM,eAAe;AAAA,IACnB,OAAO,WAAwD;AAC7D,aAAO;AACP,YAAM,KAAK,IAAI,gBAAgB;AAC/B,yBAAmB,UAAU;AAE7B,sBAAgB,IAAI;AACpB,qBAAe,KAAK;AACpB,eAAS,IAAI;AACb,qBAAe,qBAAqB;AAEpC,UAAI;AACF,cAAM,OAAO,MAAM,UAAU,QAAQ,aAAa,QAAQ;AAAA,UACxD,QAAQ,GAAG;AAAA,QACb,CAAC;AACD,gBAAQ,YAAY,IAAI;AACxB,eAAO;AAAA,MACT,SAAS,KAAc;AACrB,YAAI,GAAG,OAAO,QAAS,QAAO;AAC9B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,iBAAS,CAAC;AACV,gBAAQ,UAAU,CAAC;AACnB,eAAO;AAAA,MACT,UAAE;AACA,YAAI,mBAAmB,YAAY,IAAI;AACrC,0BAAgB,KAAK;AACrB,yBAAe,EAAE;AACjB,6BAAmB,UAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EAClB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACzNA,SAAS,YAAAA,WAAU,eAAAC,cAAa,UAAAC,SAAQ,iBAAiB;AAwDlD,SAAS,UAAU,SAA2B;AACnD,QAAM,CAAC,UAAU,WAAW,IAAIC,UAA0B,QAAQ,mBAAmB,CAAC,CAAC;AACvF,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAErD,QAAM,qBAAqBC,QAA+B,IAAI;AAC9D,QAAM,YAAYA,QAAwB,eAAe,OAAO,CAAC;AACjE,QAAM,oBAAoBA,QAAO,QAAQ,cAAc;AACvD,oBAAkB,UAAU,QAAQ;AAQpC,QAAM,iBAAiBC,aAAY,CAAC,YAA6B;AAC/D,UAAM,UAAU,kBAAkB;AAClC,QAAI,CAAC,QAAS;AACd,QAAI;AACF,WAAK,QAAQ,QAAQ,QAAQ,YAAY,OAAO,CAAC,EAAE,MAAM,MAAM,MAAS;AAAA,IAC1E,QAAQ;AAAA,IAER;AAAA,EACF,GAAG,CAAC,CAAC;AAKL,YAAU,MAAM;AACd,UAAM,UAAU,kBAAkB;AAClC,QAAI,CAAC,QAAS;AACd,QAAI,YAAY;AAChB,YAAQ,QAAQ,QAAQ,YAAY,CAAC,EAClC,KAAK,CAAC,WAAW;AAChB,UAAI,CAAC,aAAa,MAAM,QAAQ,MAAM,KAAK,OAAO,SAAS,GAAG;AAC5D,oBAAY,MAAM;AAAA,MACpB;AAAA,IACF,CAAC,EACA,MAAM,CAAC,QAAiB;AACvB,UAAI,UAAW;AACf,YAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,cAAQ,UAAU,CAAC;AAAA,IACrB,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EAIF,GAAG,CAAC,CAAC;AAEL,QAAM,SAASA,aAAY,MAAM;AAC/B,QAAI,mBAAmB,SAAS;AAG9B,yBAAmB,QAAQ,MAAM;AACjC,yBAAmB,UAAU;AAC7B,mBAAa,KAAK;AAClB,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBA,aAAY,MAAM;AACtC,gBAAY,CAAC,CAAC;AACd,aAAS,IAAI;AACb,mBAAe,CAAC,CAAC;AAAA,EACnB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,cAAcA;AAAA,IAClB,OAAO,SAAiB,gBAAoE;AAC1F,UAAI,CAAC,QAAQ,KAAK,EAAG,QAAO;AAE5B,aAAO;AACP,YAAM,KAAK,IAAI,gBAAgB;AAC/B,yBAAmB,UAAU;AAE7B,YAAM,cAA6B;AAAA,QACjC,MAAM;AAAA,QACN,SAAS,QAAQ,KAAK;AAAA,QACtB,WAAW,KAAK,IAAI;AAAA,MACtB;AAEA,YAAM,iBAAiB,CAAC,GAAG,UAAU,WAAW;AAChD,kBAAY,cAAc;AAC1B,mBAAa,IAAI;AACjB,eAAS,IAAI;AAIb,qBAAe,cAAc;AAE7B,YAAM,YAAY,aAAa,WAAW;AAE1C,UAAI;AACF,YAAI,WAAW;AACb,yBAAe,IAAI;AAGnB,cAAI,iBAAiB;AACrB,sBAAY,CAAC,SAAS;AACpB,6BAAiB,KAAK;AACtB,mBAAO,CAAC,GAAG,MAAM,EAAE,MAAM,aAAa,SAAS,IAAI,WAAW,KAAK,IAAI,EAAE,CAAC;AAAA,UAC5E,CAAC;AAED,gBAAM,eAAe,MAAM,UAAU,QAAQ;AAAA,YAC3C;AAAA,cACE,UAAU;AAAA,cACV,iBAAiB,aAAa;AAAA,cAC9B,gBAAgB,aAAa;AAAA,cAC7B,cAAc,aAAa;AAAA,YAC7B;AAAA,YACA;AAAA,cACE,aAAa,CAAC,OAAO,iBAAiB;AACpC,wBAAQ,UAAU,OAAO,YAAY;AACrC,4BAAY,CAAC,SAAS;AACpB,sBAAI,iBAAiB,KAAK,kBAAkB,KAAK,OAAQ,QAAO;AAChE,wBAAM,OAAO,KAAK,MAAM;AACxB,uBAAK,cAAc,IAAI,EAAE,GAAG,KAAK,cAAc,GAAG,SAAS,aAAa;AACxE,yBAAO;AAAA,gBACT,CAAC;AAAA,cACH;AAAA,cACA,gBAAgB,CAAC,YAAY;AAC3B,4BAAY,CAAC,SAAS;AACpB,sBAAI,iBAAiB,KAAK,kBAAkB,KAAK,OAAQ,QAAO;AAChE,wBAAM,OAAO,KAAK,MAAM;AACxB,uBAAK,cAAc,IAAI;AACvB,yBAAO;AAAA,gBACT,CAAC;AAAA,cACH;AAAA,YACF;AAAA,YACA,EAAE,QAAQ,GAAG,OAAO;AAAA,UACtB;AAKA,yBAAe,CAAC,GAAG,gBAAgB,YAAY,CAAC;AAChD,kBAAQ,YAAY,YAAY;AAChC,iBAAO;AAAA,QACT;AAEA,cAAM,WAA2B,MAAM,UAAU,QAAQ;AAAA,UACvD;AAAA,YACE,UAAU;AAAA,YACV,iBAAiB,aAAa;AAAA,YAC9B,gBAAgB,aAAa;AAAA,YAC7B,cAAc,aAAa;AAAA,UAC7B;AAAA,UACA,EAAE,QAAQ,GAAG,OAAO;AAAA,QACtB;AAEA,cAAM,eAAe,SAAS;AAC9B,oBAAY,CAAC,SAAS,CAAC,GAAG,MAAM,YAAY,CAAC;AAG7C,uBAAe,CAAC,GAAG,gBAAgB,YAAY,CAAC;AAChD,gBAAQ,YAAY,YAAY;AAChC,eAAO;AAAA,MACT,SAAS,KAAc;AACrB,YAAI,GAAG,OAAO,QAAS,QAAO;AAC9B,cAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAC5D,iBAAS,CAAC;AACV,gBAAQ,UAAU,CAAC;AACnB,eAAO;AAAA,MACT,UAAE;AACA,YAAI,mBAAmB,YAAY,IAAI;AACrC,uBAAa,KAAK;AAClB,yBAAe,KAAK;AACpB,6BAAmB,UAAU;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,UAAU,SAAS,cAAc;AAAA,EAC5C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA;AAAA,IAEA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACpPA,SAAS,eAAAC,cAAa,UAAAC,SAAQ,YAAAC,iBAAgB;AAsCvC,SAAS,WAAW,SAA4B;AACrD,QAAM,CAAC,WAAW,YAAY,IAAIC,UAAS,KAAK;AAChD,QAAM,CAAC,MAAM,OAAO,IAAIA,UAAS,CAAC;AAClC,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,CAAC;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAA+B,CAAC,CAAC;AACjE,QAAM,CAAC,KAAK,MAAM,IAAIA,UAA0B,CAAC,CAAC;AAClD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAiB,EAAE;AACjD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAkC,IAAI;AAClE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAErD,QAAM,qBAAqBC,QAA+B,IAAI;AAC9D,QAAM,YAAYA,QAAwB,eAAe,OAAO,CAAC;AACjE,QAAM,aAAaA,QAAO,OAAO;AACjC,aAAW,UAAU;AAErB,QAAM,QAAQC,aAAY,MAAM;AAC9B,gBAAY,CAAC,CAAC;AACd,WAAO,CAAC,CAAC;AACT,eAAW,EAAE;AACb,cAAU,IAAI;AACd,aAAS,IAAI;AACb,YAAQ,CAAC;AACT,gBAAY,CAAC;AAAA,EACf,GAAG,CAAC,CAAC;AAEL,QAAM,QAAQA,aAAY,MAAM;AAC9B,uBAAmB,SAAS,MAAM;AAClC,uBAAmB,UAAU;AAC7B,iBAAa,KAAK;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,QAAM,MAAMA;AAAA,IACV,OAAO,WAA6D;AAGlE,yBAAmB,SAAS,MAAM;AAClC,YAAM,aAAa,IAAI,gBAAgB;AACvC,yBAAmB,UAAU;AAE7B,YAAM;AACN,mBAAa,IAAI;AAEjB,YAAM,WAA4B;AAAA,QAChC,GAAI,OAAO,WAAW,CAAC;AAAA,QACvB,EAAE,MAAM,QAAQ,SAAS,OAAO,aAAa,WAAW,KAAK,IAAI,EAAE;AAAA,MACrE;AAEA,UAAI;AACF,cAAM,YAAY,MAAM,UAAU,QAAQ;AAAA,UACxC;AAAA,YACE;AAAA,YACA,UAAU,OAAO;AAAA,YACjB,gBAAgB,OAAO;AAAA,YACvB,iBAAiB,OAAO;AAAA,YACxB,UAAU,OAAO;AAAA,UACnB;AAAA,UACA;AAAA,YACE,aAAa,CAAC,aAAa,UAAU;AACnC,sBAAQ,WAAW;AACnB,0BAAY,KAAK;AAAA,YACnB;AAAA,YACA,YAAY,CAAC,SAAS;AACpB,0BAAY,CAAC,SAAS;AAAA,gBACpB,GAAG;AAAA,gBACH,EAAE,IAAI,KAAK,IAAI,MAAM,KAAK,MAAM,OAAO,KAAK,OAAO,QAAQ,UAAU;AAAA,cACvE,CAAC;AAAA,YACH;AAAA,YACA,cAAc,CAAC,eAAe;AAC5B;AAAA,gBAAY,CAAC,SACX,KAAK;AAAA,kBAAI,CAAC,UACR,MAAM,OAAO,WAAW,KACpB,EAAE,GAAG,OAAO,QAAQ,WAAW,KAAK,OAAO,UAAU,SAAS,WAAW,QAAQ,IACjF;AAAA,gBACN;AAAA,cACF;AAAA,YACF;AAAA,YACA,iBAAiB,CAAC,cAAc;AAC9B,qBAAO,UAAU,GAAG;AACpB,yBAAW,UAAU,OAAO;AAC5B,wBAAU,SAAS;AACnB,yBAAW,QAAQ,aAAa,SAAS;AAAA,YAC3C;AAAA,YACA,SAAS,CAAC,QAAQ;AAChB,uBAAS,GAAG;AACZ,yBAAW,QAAQ,UAAU,GAAG;AAAA,YAClC;AAAA,UACF;AAAA,UACA,EAAE,QAAQ,WAAW,OAAO;AAAA,QAC9B;AAEA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,cAAM,aAAa,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAErE,YAAI,WAAW,SAAS,cAAc;AACpC,mBAAS,UAAU;AACnB,qBAAW,QAAQ,UAAU,UAAU;AAAA,QACzC;AACA,eAAO;AAAA,MACT,UAAE;AACA,qBAAa,KAAK;AAClB,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,IACA,CAAC,KAAK;AAAA,EACR;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["useState","useCallback","useRef","useState","useRef","useCallback","useCallback","useRef","useState","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 PagePlan,\n AiPlanPageRequest,\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 /**\n * Plans website structure (sections overview) before generating.\n */\n const planPage = useCallback(\n async (params: AiPlanPageRequest): Promise<PagePlan | null> => {\n cancel();\n const ac = new AbortController();\n abortControllerRef.current = ac;\n\n setIsGenerating(true);\n setIsStreaming(false);\n setError(null);\n setCurrentStep('Planning page structure...');\n\n try {\n const plan = await clientRef.current.planPage(params, {\n signal: ac.signal,\n });\n return plan;\n } catch (err: unknown) {\n const e = err instanceof Error ? err : new Error(String(err));\n setError(e);\n options.onError?.(e);\n throw e;\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 planPage,\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"]}
@@ -1,5 +1,5 @@
1
1
  import type { PageDocument, Node } from '@kubuild/schema';
2
- import type { AiGeneratePageRequest, AiGenerateSectionRequest, AiRefactorNodeRequest, AiStreamCallbacks } from '../types';
2
+ import type { AiGeneratePageRequest, AiGenerateSectionRequest, AiRefactorNodeRequest, AiStreamCallbacks, PagePlan, AiPlanPageRequest } from '../types';
3
3
  import { type AiClientOptions } from '../client/ai-client';
4
4
  export interface UseAiGeneratorOptions extends AiClientOptions {
5
5
  onSuccess?: (data: PageDocument | Node) => void;
@@ -7,6 +7,7 @@ export interface UseAiGeneratorOptions extends AiClientOptions {
7
7
  }
8
8
  export declare function useAiGenerator(options: UseAiGeneratorOptions): {
9
9
  generatePage: (params: AiGeneratePageRequest) => Promise<PageDocument | null>;
10
+ planPage: (params: AiPlanPageRequest) => Promise<PagePlan | null>;
10
11
  streamPage: (params: AiGeneratePageRequest, streamCallbacks?: AiStreamCallbacks) => Promise<PageDocument | null>;
11
12
  generateSection: (params: AiGenerateSectionRequest) => Promise<Node | null>;
12
13
  refactorNode: (params: AiRefactorNodeRequest) => Promise<Node | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"use-ai-generator.d.ts","sourceRoot":"","sources":["../../src/react/use-ai-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAkB,KAAK,eAAe,EAAmB,MAAM,qBAAqB,CAAC;AAE5F,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB;2BAwB1C,qBAAqB,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;yBAuCzD,qBAAqB,oBACX,iBAAiB,KAClC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;8BA2DhB,wBAAwB,KAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;2BAkC/C,qBAAqB,KAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;;;;;;EA4C9D"}
1
+ {"version":3,"file":"use-ai-generator.d.ts","sourceRoot":"","sources":["../../src/react/use-ai-generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EACV,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,iBAAiB,EACjB,QAAQ,EACR,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAkB,KAAK,eAAe,EAAmB,MAAM,qBAAqB,CAAC;AAE5F,MAAM,WAAW,qBAAsB,SAAQ,eAAe;IAC5D,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB;2BAwB1C,qBAAqB,KAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;uBA2KpD,iBAAiB,KAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;yBApIjD,qBAAqB,oBACX,iBAAiB,KAClC,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;8BA2DhB,wBAAwB,KAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;2BAkC/C,qBAAqB,KAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;;;;;;EAgF9D"}
@@ -1,5 +1,5 @@
1
1
  import { PageDocument, Node } from '@kubuild/schema';
2
- import type { KubuildAiEngineOptions, AiGeneratePageRequest, AiGenerateSectionRequest, AiRefactorNodeRequest, AiChatRequest, AiChatResponse, AiGenerateResponse, AiStreamEvent } from '../types';
2
+ import type { KubuildAiEngineOptions, AiGeneratePageRequest, AiGenerateSectionRequest, AiRefactorNodeRequest, AiChatRequest, AiChatResponse, AiGenerateResponse, AiStreamEvent, PagePlan, AiPlanPageRequest } from '../types';
3
3
  export declare class KubuildAiEngine {
4
4
  private options;
5
5
  private catalog;
@@ -16,6 +16,10 @@ export declare class KubuildAiEngine {
16
16
  refactorNode(request: AiRefactorNodeRequest, context?: {
17
17
  signal?: AbortSignal;
18
18
  }): Promise<AiGenerateResponse<Node>>;
19
+ private buildPlanPrompts;
20
+ planPage(request: AiPlanPageRequest, context?: {
21
+ signal?: AbortSignal;
22
+ }): Promise<AiGenerateResponse<PagePlan>>;
19
23
  /**
20
24
  * Progressive Section Streaming Generator.
21
25
  * Emits structured SSE events: status -> metadata -> section (one by one) -> complete.
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/server/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAA4C,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,cAAc,EAEd,kBAAkB,EAElB,aAAa,EACd,MAAM,UAAU,CAAC;AA2BlB,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,EAAE,sBAAsB;IAQ3C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,cAAc,IAAI,IAAI;IAItB,OAAO,CAAC,GAAG;IASL,YAAY,CAChB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAkEtC,eAAe,CACnB,OAAO,EAAE,wBAAwB,EACjC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAuD9B,YAAY,CAChB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IA+CpC;;;OAGG;IACI,UAAU,CACf,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,aAAa,CAAC,aAAa,CAAC;IAiN/B;;;;OAIG;IACH,OAAO,CAAC,eAAe;IA4CvB;;;;;;;OAOG;IACG,IAAI,CACR,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAsD9C;;;;;;;;;;;OAWG;IACI,UAAU,CACf,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,aAAa,CAAC,aAAa,CAAC;CA4EhC"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/server/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,IAAI,EAA4C,MAAM,iBAAiB,CAAC;AAC/F,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,qBAAqB,EACrB,aAAa,EACb,cAAc,EAEd,kBAAkB,EAElB,aAAa,EACb,QAAQ,EAER,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAclB,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAAyB;IACxC,OAAO,CAAC,OAAO,CAA4B;gBAE/B,OAAO,EAAE,sBAAsB;IAQ3C,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,cAAc,IAAI,IAAI;IAItB,OAAO,CAAC,GAAG;IASL,YAAY,CAChB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;IAwEtC,eAAe,CACnB,OAAO,EAAE,wBAAwB,EACjC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IAuD9B,YAAY,CAChB,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC;IA+CpC,OAAO,CAAC,gBAAgB;IAsDlB,QAAQ,CACZ,OAAO,EAAE,iBAAiB,EAC1B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IA2CxC;;;OAGG;IACI,UAAU,CACf,OAAO,EAAE,qBAAqB,EAC9B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,aAAa,CAAC,aAAa,CAAC;IA6L/B;;;;OAIG;IACH,OAAO,CAAC,eAAe;IA4CvB;;;;;;;OAOG;IACG,IAAI,CACR,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,OAAO,CAAC,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAsD9C;;;;;;;;;;;OAWG;IACI,UAAU,CACf,OAAO,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACjC,aAAa,CAAC,aAAa,CAAC;CA4EhC"}
@@ -1,7 +1,7 @@
1
1
  import type { KubuildAiEngine } from './engine';
2
2
  import type { KubuildAiAgent } from './agent';
3
3
  import type { PageDocument } from '@kubuild/schema';
4
- import type { AiGeneratePageRequest, AiRefactorNodeRequest, AiChatMessage, AiGenerateResponse, AiGenerationMode } from '../types';
4
+ import type { AiGeneratePageRequest, AiRefactorNodeRequest, AiChatMessage, AiGenerateResponse, AiGenerationMode, PagePlan } from '../types';
5
5
  export interface AiApiRequestBody {
6
6
  mode?: AiGenerationMode;
7
7
  prompt?: string;
@@ -17,6 +17,13 @@ export interface AiApiRequestBody {
17
17
  messages?: AiChatMessage[];
18
18
  currentDocument?: PageDocument;
19
19
  selectedNodeId?: string;
20
+ /** Prior conversation history from chat mode so generator keeps full context. */
21
+ conversationHistory?: AiChatMessage[];
22
+ sectionCount?: number | {
23
+ min?: number;
24
+ max?: number;
25
+ };
26
+ plan?: PagePlan;
20
27
  /** Agent mode (STORA-530) — the snapshot the agent reads and patches. */
21
28
  document?: PageDocument;
22
29
  maxSteps?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/server/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACV,qBAAqB,EAErB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAEjB,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,WAAW,EACpB,KAAK,CAAC,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAiKpE;AAsDD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IAEvB;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC;CAClF;AA8CD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,sBAAsB,IACzE,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA4JnD"}
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/server/handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EACV,qBAAqB,EAErB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAEhB,QAAQ,EACT,MAAM,UAAU,CAAC;AAElB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACrC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,eAAe,CAAC,EAAE,YAAY,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,aAAa,EAAE,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACvD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,YAAY,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,WAAW,EACpB,KAAK,CAAC,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,kBAAkB,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAkMpE;AAsDD;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,KAAK,CAAC,EAAE,cAAc,CAAC;IAEvB;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,QAAQ,GAAG,IAAI,CAAC;CAClF;AA8CD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,sBAAsB,IACzE,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA+JnD"}
@@ -735,6 +735,13 @@ var KubuildAiEngine = class {
735
735
  Tone: ${request.tone}`;
736
736
  if (request.locale) userPrompt += `
737
737
  Language/Locale: ${request.locale}`;
738
+ if (request.conversationHistory && request.conversationHistory.length > 0) {
739
+ const chatContext = request.conversationHistory.map((m) => `${m.role.toUpperCase()}: ${getMessageText(m)}`).join("\n");
740
+ userPrompt += `
741
+
742
+ Prior Conversation Discussion Context:
743
+ ${chatContext}`;
744
+ }
738
745
  const jsonSchema = buildJsonSchemaForMode("full-page");
739
746
  const result = await this.options.adapter.generate({
740
747
  systemPrompt,
@@ -873,18 +880,18 @@ ${JSON.stringify(request.node, null, 2)}`;
873
880
  };
874
881
  }
875
882
  }
876
- /**
877
- * Progressive Section Streaming Generator.
878
- * Emits structured SSE events: status -> metadata -> section (one by one) -> complete.
879
- */
880
- async *streamPage(request, context) {
881
- try {
882
- this.log("info", `[SSE] Starting streamPage for prompt: "${request.prompt}"`);
883
- yield {
884
- type: "status",
885
- message: "Analyzing requirements and planning page sections..."
886
- };
887
- const planSystemPrompt = `
883
+ buildPlanPrompts(request) {
884
+ let sectionGuidance = "Plan between 4 to 6 cohesive, essential sections (e.g., hero, features, testimonials, pricing, cta, footer) that fulfill the request thoroughly.";
885
+ if (typeof request.sectionCount === "number") {
886
+ sectionGuidance = `Plan exactly ${request.sectionCount} cohesive sections that fulfill the request.`;
887
+ } else if (request.sectionCount?.min || request.sectionCount?.max) {
888
+ const min = request.sectionCount.min ?? 3;
889
+ const max = request.sectionCount.max ?? 8;
890
+ sectionGuidance = `Plan between ${min} and ${max} cohesive sections that fulfill the request.`;
891
+ } else {
892
+ sectionGuidance += " If the user request mentions a specific number or list of sections, respect the user request.";
893
+ }
894
+ const planSystemPrompt = `
888
895
  You are a web architect for the KUBUILD page builder.
889
896
  Given the user's prompt, plan the website structure. Output pure JSON (no markdown fences, no explanatory text):
890
897
  {
@@ -906,44 +913,113 @@ Given the user's prompt, plan the website structure. Output pure JSON (no markdo
906
913
  }
907
914
  ]
908
915
  }
909
- Plan 3 cohesive, essential sections (e.g., hero, features, and cta) that fulfill the request.
916
+ ${sectionGuidance}
910
917
  `;
911
- let planUserPrompt = `User Request: ${request.prompt}`;
912
- if (request.stylePreference) {
913
- planUserPrompt += `
918
+ let planUserPrompt = `User Request: ${request.prompt}`;
919
+ if (request.stylePreference) {
920
+ planUserPrompt += `
914
921
  Style Preference: ${request.stylePreference}`;
915
- }
916
- if (request.tone) planUserPrompt += `
922
+ }
923
+ if (request.tone) planUserPrompt += `
917
924
  Tone: ${request.tone}`;
918
- if (request.locale) planUserPrompt += `
925
+ if (request.locale) planUserPrompt += `
919
926
  Locale: ${request.locale}`;
920
- this.log("info", "[SSE] Generating website layout plan...");
927
+ if (request.conversationHistory && request.conversationHistory.length > 0) {
928
+ const chatContext = request.conversationHistory.map((m) => `${m.role.toUpperCase()}: ${getMessageText(m)}`).join("\n");
929
+ planUserPrompt += `
930
+
931
+ Prior Conversation Discussion Context:
932
+ ${chatContext}`;
933
+ }
934
+ return { systemPrompt: planSystemPrompt, userPrompt: planUserPrompt };
935
+ }
936
+ async planPage(request, context) {
937
+ let rawText = "";
938
+ try {
939
+ const { systemPrompt, userPrompt } = this.buildPlanPrompts(request);
940
+ this.log("info", `Planning website layout for: "${request.prompt}"`);
921
941
  const planResult = await this.options.adapter.generate({
922
- systemPrompt: planSystemPrompt,
923
- userPrompt: planUserPrompt,
942
+ systemPrompt,
943
+ userPrompt,
924
944
  signal: context?.signal
925
945
  });
926
- this.log("debug", "[SSE] Raw plan response from model", planResult.text);
946
+ rawText = planResult.text;
947
+ const plan = extractJsonFromResponse(rawText);
948
+ return {
949
+ success: true,
950
+ data: plan,
951
+ usage: planResult.usage,
952
+ rawModelResponse: rawText
953
+ };
954
+ } catch (err) {
955
+ const message = err instanceof Error ? err.message : String(err);
956
+ this.log("warn", "Failed to generate plan JSON, using fallback plan", message);
957
+ const fallbackPlan = {
958
+ title: "AI Generated Page",
959
+ description: "Generated by KUBUILD AI",
960
+ sections: [
961
+ { type: "hero", title: "Hero Banner", prompt: `Hero section for: ${request.prompt}` },
962
+ { type: "features", title: "Key Features", prompt: `Features grid for: ${request.prompt}` },
963
+ { type: "testimonials", title: "Testimonials", prompt: `Social proof and customer reviews for: ${request.prompt}` },
964
+ { type: "cta", title: "Call To Action", prompt: `Call to action section for: ${request.prompt}` },
965
+ { type: "footer", title: "Footer", prompt: `Footer navigation and copyright for: ${request.prompt}` }
966
+ ]
967
+ };
968
+ return {
969
+ success: true,
970
+ data: fallbackPlan,
971
+ rawModelResponse: rawText || void 0
972
+ };
973
+ }
974
+ }
975
+ /**
976
+ * Progressive Section Streaming Generator.
977
+ * Emits structured SSE events: status -> metadata -> section (one by one) -> complete.
978
+ */
979
+ async *streamPage(request, context) {
980
+ try {
981
+ this.log("info", `[SSE] Starting streamPage for prompt: "${request.prompt}"`);
982
+ yield {
983
+ type: "status",
984
+ message: "Analyzing requirements and planning page sections..."
985
+ };
927
986
  let plan;
928
- try {
929
- plan = extractJsonFromResponse(planResult.text);
930
- this.log("debug", "[SSE] Parsed plan successfully", plan);
931
- } catch (parseErr) {
932
- this.log("warn", "[SSE] Failed to parse plan JSON, using fallback plan", parseErr);
933
- plan = {
934
- title: "AI Generated Page",
935
- description: "Generated by KUBUILD AI",
936
- sections: [
937
- { type: "hero", title: "Hero Banner", prompt: `Hero section for: ${request.prompt}` },
938
- { type: "features", title: "Key Features", prompt: `Features grid for: ${request.prompt}` },
939
- { type: "cta", title: "Call To Action", prompt: `Call to action section for: ${request.prompt}` }
940
- ]
941
- };
987
+ if (request.plan && Array.isArray(request.plan.sections) && request.plan.sections.length > 0) {
988
+ plan = request.plan;
989
+ this.log("info", `[SSE] Using approved pre-planned structure with ${plan.sections?.length ?? 0} sections`);
990
+ } else {
991
+ const { systemPrompt: planSystemPrompt, userPrompt: planUserPrompt } = this.buildPlanPrompts(request);
992
+ this.log("info", "[SSE] Generating website layout plan...");
993
+ const planResult = await this.options.adapter.generate({
994
+ systemPrompt: planSystemPrompt,
995
+ userPrompt: planUserPrompt,
996
+ signal: context?.signal
997
+ });
998
+ this.log("debug", "[SSE] Raw plan response from model", planResult.text);
999
+ try {
1000
+ plan = extractJsonFromResponse(planResult.text);
1001
+ this.log("debug", "[SSE] Parsed plan successfully", plan);
1002
+ } catch (parseErr) {
1003
+ this.log("warn", "[SSE] Failed to parse plan JSON, using fallback plan", parseErr);
1004
+ plan = {
1005
+ title: "AI Generated Page",
1006
+ description: "Generated by KUBUILD AI",
1007
+ sections: [
1008
+ { type: "hero", title: "Hero Banner", prompt: `Hero section for: ${request.prompt}` },
1009
+ { type: "features", title: "Key Features", prompt: `Features grid for: ${request.prompt}` },
1010
+ { type: "testimonials", title: "Testimonials", prompt: `Social proof for: ${request.prompt}` },
1011
+ { type: "cta", title: "Call To Action", prompt: `Call to action section for: ${request.prompt}` },
1012
+ { type: "footer", title: "Footer", prompt: `Footer for: ${request.prompt}` }
1013
+ ]
1014
+ };
1015
+ }
942
1016
  }
943
1017
  const sectionsToGenerate = Array.isArray(plan.sections) && plan.sections.length > 0 ? plan.sections : [
944
1018
  { type: "hero", title: "Hero Section", prompt: `Hero banner for: ${request.prompt}` },
945
1019
  { type: "features", title: "Features", prompt: `Features grid for: ${request.prompt}` },
946
- { type: "cta", title: "Call To Action", prompt: `CTA section for: ${request.prompt}` }
1020
+ { type: "testimonials", title: "Testimonials", prompt: `Social proof for: ${request.prompt}` },
1021
+ { type: "cta", title: "Call To Action", prompt: `CTA section for: ${request.prompt}` },
1022
+ { type: "footer", title: "Footer", prompt: `Footer section for: ${request.prompt}` }
947
1023
  ];
948
1024
  const metadata = import_schema2.DocumentMetadataSchema.parse({
949
1025
  title: plan.title || "AI Generated Page",
@@ -2325,7 +2401,10 @@ async function processAiRequest(engine, body, signal, agent) {
2325
2401
  stylePreference: payload.stylePreference,
2326
2402
  tone: payload.tone,
2327
2403
  locale: payload.locale,
2328
- metadata: payload.metadata
2404
+ metadata: payload.metadata,
2405
+ conversationHistory: payload.conversationHistory ?? payload.messages,
2406
+ sectionCount: payload.sectionCount,
2407
+ plan: payload.plan
2329
2408
  },
2330
2409
  { signal }
2331
2410
  );
@@ -2433,13 +2512,42 @@ async function processAiRequest(engine, body, signal, agent) {
2433
2512
  response: { success: result.stoppedBy !== "error", data: result }
2434
2513
  };
2435
2514
  }
2515
+ if (mode === "plan") {
2516
+ if (!payload.prompt || typeof payload.prompt !== "string") {
2517
+ return {
2518
+ status: 400,
2519
+ response: {
2520
+ success: false,
2521
+ error: {
2522
+ code: "INVALID_PROMPT",
2523
+ message: '"prompt" is required for planning mode'
2524
+ }
2525
+ }
2526
+ };
2527
+ }
2528
+ const result = await engine.planPage(
2529
+ {
2530
+ prompt: payload.prompt,
2531
+ stylePreference: payload.stylePreference,
2532
+ tone: payload.tone,
2533
+ locale: payload.locale,
2534
+ sectionCount: payload.sectionCount,
2535
+ conversationHistory: payload.conversationHistory ?? payload.messages
2536
+ },
2537
+ { signal }
2538
+ );
2539
+ return {
2540
+ status: result.success ? 200 : 500,
2541
+ response: result
2542
+ };
2543
+ }
2436
2544
  return {
2437
2545
  status: 400,
2438
2546
  response: {
2439
2547
  success: false,
2440
2548
  error: {
2441
2549
  code: "UNKNOWN_MODE",
2442
- message: `Unsupported mode: ${String(mode)}. Supported modes: 'full-page', 'section', 'refactor', 'chat', 'agent'`
2550
+ message: `Unsupported mode: ${String(mode)}. Supported modes: 'full-page', 'section', 'refactor', 'chat', 'agent', 'plan'`
2443
2551
  }
2444
2552
  }
2445
2553
  };
@@ -2622,7 +2730,10 @@ function createAiHandler(engine, options) {
2622
2730
  stylePreference: body.stylePreference,
2623
2731
  tone: body.tone,
2624
2732
  locale: body.locale,
2625
- metadata: body.metadata
2733
+ metadata: body.metadata,
2734
+ conversationHistory: body.conversationHistory ?? body.messages,
2735
+ sectionCount: body.sectionCount,
2736
+ plan: body.plan
2626
2737
  },
2627
2738
  { signal: request.signal }
2628
2739
  ),