@copilotkit/web-inspector 1.68.3 → 1.69.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -12
- package/dist/assets/inspector-logo-kite.cjs +12 -0
- package/dist/assets/inspector-logo-kite.cjs.map +1 -0
- package/dist/assets/inspector-logo-kite.mjs +6 -0
- package/dist/assets/inspector-logo-kite.mjs.map +1 -0
- package/dist/index.cjs +5574 -1584
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +490 -32
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +490 -32
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5574 -1587
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +7014 -1917
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/event-snippets.cjs +559 -0
- package/dist/lib/event-snippets.cjs.map +1 -0
- package/dist/lib/event-snippets.d.cts +61 -0
- package/dist/lib/event-snippets.d.cts.map +1 -0
- package/dist/lib/event-snippets.d.mts +61 -0
- package/dist/lib/event-snippets.d.mts.map +1 -0
- package/dist/lib/event-snippets.mjs +537 -0
- package/dist/lib/event-snippets.mjs.map +1 -0
- package/dist/lib/home-briefing.cjs +290 -0
- package/dist/lib/home-briefing.cjs.map +1 -0
- package/dist/lib/home-briefing.mjs +288 -0
- package/dist/lib/home-briefing.mjs.map +1 -0
- package/dist/lib/inspector-nav.cjs +59 -0
- package/dist/lib/inspector-nav.cjs.map +1 -0
- package/dist/lib/inspector-nav.d.cts +11 -0
- package/dist/lib/inspector-nav.d.cts.map +1 -0
- package/dist/lib/inspector-nav.d.mts +11 -0
- package/dist/lib/inspector-nav.d.mts.map +1 -0
- package/dist/lib/inspector-nav.mjs +54 -0
- package/dist/lib/inspector-nav.mjs.map +1 -0
- package/dist/lib/persistence.cjs +102 -0
- package/dist/lib/persistence.cjs.map +1 -1
- package/dist/lib/persistence.mjs +98 -1
- package/dist/lib/persistence.mjs.map +1 -1
- package/dist/lib/telemetry.cjs +93 -34
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.d.cts +1 -1
- package/dist/lib/telemetry.d.cts.map +1 -1
- package/dist/lib/telemetry.d.mts +1 -1
- package/dist/lib/telemetry.d.mts.map +1 -1
- package/dist/lib/telemetry.mjs +86 -32
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/styles/generated.cjs +1 -1
- package/dist/styles/generated.cjs.map +1 -1
- package/dist/styles/generated.mjs +1 -1
- package/dist/styles/generated.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/assets/inspector-logo-icon.cjs +0 -12
- package/dist/assets/inspector-logo-icon.cjs.map +0 -1
- package/dist/assets/inspector-logo-icon.mjs +0 -6
- package/dist/assets/inspector-logo-icon.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-snippets.mjs","names":[],"sources":["../../src/lib/event-snippets.ts"],"sourcesContent":["export const EVENT_SNIPPETS_STORAGE_KEY = \"cpk:inspector:event-snippets\";\n\nexport const SNIPPET_RECIPES = [\n \"tool-call\",\n \"reasoning\",\n \"text\",\n \"activity\",\n \"raw\",\n] as const;\n\nexport type SnippetRecipe = (typeof SNIPPET_RECIPES)[number];\n\nexport type SnippetEvent = {\n type: string;\n [key: string]: unknown;\n};\n\nexport type EventSnippet = {\n id: string;\n name: string;\n recipe: SnippetRecipe;\n events: SnippetEvent[];\n createdAt: string;\n updatedAt: string;\n};\n\nexport type LastInject = {\n snippetId: string;\n agentId: string;\n runId: string;\n messageIds: string[];\n};\n\nexport type AssistantSnippetMessage = {\n id: string;\n content?: string | null;\n toolCalls?: ReadonlyArray<{\n id: string;\n function: { name: string; arguments: string };\n }>;\n};\n\nexport type ActivitySnippetMessage = {\n id: string;\n activityType: string;\n content: unknown;\n};\n\nexport const ACTIVITY_STARTERS = [\n \"a2ui-surface\",\n \"open-generative-ui\",\n] as const;\n\nexport function isSnippetRecipe(value: unknown): value is SnippetRecipe {\n return (\n typeof value === \"string\" &&\n SNIPPET_RECIPES.some((recipe) => recipe === value)\n );\n}\n\nexport function createSnippetId(): string {\n if (typeof crypto !== \"undefined\" && \"randomUUID\" in crypto) {\n return crypto.randomUUID();\n }\n return `cpk-snippet-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n\nexport function autoSnippetName(recipe: SnippetRecipe, label: string): string {\n const time = new Date().toLocaleTimeString([], {\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n const base = label.trim() || recipeLabel(recipe);\n return `${base} - ${time}`;\n}\n\nexport function recipeLabel(recipe: SnippetRecipe): string {\n switch (recipe) {\n case \"tool-call\":\n return \"Tool call\";\n case \"reasoning\":\n return \"Reasoning\";\n case \"text\":\n return \"Assistant text\";\n case \"activity\":\n return \"Activity\";\n case \"raw\":\n return \"Raw events\";\n }\n}\n\nexport const GENERATE_SANDBOXED_UI_TOOL_NAME = \"generateSandboxedUi\";\nexport const OPEN_GENERATIVE_UI_ACTIVITY_TYPE = \"open-generative-ui\";\n\nexport function recipeIconName(recipe: SnippetRecipe): string {\n switch (recipe) {\n case \"tool-call\":\n return \"Hammer\";\n case \"reasoning\":\n return \"Brain\";\n case \"text\":\n return \"MessageSquare\";\n case \"activity\":\n return \"LayoutDashboard\";\n case \"raw\":\n return \"Code\";\n }\n}\n\nexport function recipeIconWrapClass(recipe: SnippetRecipe): string {\n switch (recipe) {\n case \"tool-call\":\n return \"bg-amber-100 text-amber-700\";\n case \"reasoning\":\n return \"bg-violet-100 text-violet-700\";\n case \"text\":\n return \"bg-sky-100 text-sky-700\";\n case \"activity\":\n return \"bg-emerald-100 text-emerald-700\";\n case \"raw\":\n return \"bg-gray-200 text-gray-700\";\n }\n}\n\nexport function groupEventSnippets(\n snippets: ReadonlyArray<EventSnippet>,\n): Array<{\n recipe: SnippetRecipe;\n label: string;\n snippets: EventSnippet[];\n}> {\n return SNIPPET_RECIPES.flatMap((recipe) => {\n const items = snippets.filter((snippet) => snippet.recipe === recipe);\n if (items.length === 0) {\n return [];\n }\n return [{ recipe, label: recipeLabel(recipe), snippets: items }];\n });\n}\n\nexport function snippetJsonIsRunnable(raw: string): boolean {\n try {\n return parseSnippetEvents(raw).length > 0;\n } catch {\n return false;\n }\n}\n\nexport function expandSnippetEventsForRun(\n events: ReadonlyArray<SnippetEvent>,\n): SnippetEvent[] {\n const next = [...events];\n const hasOpenGenUi = next.some(\n (event) =>\n event.type === \"ACTIVITY_SNAPSHOT\" &&\n event.activityType === OPEN_GENERATIVE_UI_ACTIVITY_TYPE,\n );\n\n const toolCallIds: string[] = [];\n for (const event of next) {\n if (\n event.type === \"TOOL_CALL_START\" &&\n event.toolCallName === GENERATE_SANDBOXED_UI_TOOL_NAME &&\n typeof event.toolCallId === \"string\"\n ) {\n toolCallIds.push(event.toolCallId);\n }\n }\n if (toolCallIds.length === 0) {\n return next;\n }\n\n const argParts = new Map<string, string[]>();\n for (const event of next) {\n if (\n event.type === \"TOOL_CALL_ARGS\" &&\n typeof event.toolCallId === \"string\" &&\n typeof event.delta === \"string\"\n ) {\n const parts = argParts.get(event.toolCallId) ?? [];\n parts.push(event.delta);\n argParts.set(event.toolCallId, parts);\n }\n }\n\n const extras: SnippetEvent[] = [];\n for (const toolCallId of toolCallIds) {\n let addedSnapshot = false;\n if (!hasOpenGenUi) {\n const snapshot = activitySnapshotFromSandboxedUiArgs(\n toolCallId,\n (argParts.get(toolCallId) ?? []).join(\"\"),\n );\n if (snapshot) {\n extras.push(snapshot);\n addedSnapshot = true;\n }\n }\n if (!hasOpenGenUi && !addedSnapshot) {\n continue;\n }\n const hasResult = next.some(\n (event) =>\n event.type === \"TOOL_CALL_RESULT\" && event.toolCallId === toolCallId,\n );\n if (!hasResult) {\n extras.push({\n type: \"TOOL_CALL_RESULT\",\n toolCallId,\n messageId: `${toolCallId}-result`,\n role: \"tool\",\n content: \"UI generated\",\n });\n }\n }\n if (extras.length === 0) {\n return next;\n }\n\n const finishIndex = next.findIndex((event) => event.type === \"RUN_FINISHED\");\n if (finishIndex === -1) {\n return [...next, ...extras];\n }\n return [...next.slice(0, finishIndex), ...extras, ...next.slice(finishIndex)];\n}\n\nfunction activitySnapshotFromSandboxedUiArgs(\n toolCallId: string,\n rawArgs: string,\n): SnippetEvent | null {\n let args: { [key: string]: unknown };\n try {\n const parsed: unknown = JSON.parse(snippetArgsJson(rawArgs));\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n args = parsed as { [key: string]: unknown };\n } catch {\n return null;\n }\n\n const html = sandboxedUiHtmlChunks(args.html);\n const css = typeof args.css === \"string\" ? args.css : undefined;\n const jsFunctions =\n typeof args.jsFunctions === \"string\" ? args.jsFunctions : undefined;\n const jsExpressions = Array.isArray(args.jsExpressions)\n ? args.jsExpressions.filter(\n (item): item is string => typeof item === \"string\",\n )\n : undefined;\n if (\n html.length === 0 &&\n css === undefined &&\n jsFunctions === undefined &&\n (jsExpressions === undefined || jsExpressions.length === 0)\n ) {\n return null;\n }\n\n return {\n type: \"ACTIVITY_SNAPSHOT\",\n messageId: `${toolCallId}-activity`,\n activityType: OPEN_GENERATIVE_UI_ACTIVITY_TYPE,\n content: {\n initialHeight:\n typeof args.initialHeight === \"number\" ? args.initialHeight : undefined,\n generating: false,\n css,\n cssComplete: true,\n html,\n htmlComplete: true,\n jsFunctions,\n jsFunctionsComplete: true,\n jsExpressions,\n jsExpressionsComplete: true,\n },\n replace: true,\n };\n}\n\nfunction sandboxedUiHtmlChunks(html: unknown): string[] {\n if (typeof html === \"string\" && html.length > 0) {\n return [html];\n }\n if (Array.isArray(html)) {\n return html.filter((item): item is string => typeof item === \"string\");\n }\n return [];\n}\n\nexport function snippetContainsToolCall(\n events: ReadonlyArray<SnippetEvent>,\n): boolean {\n return events.some(\n (event) =>\n typeof event.type === \"string\" && event.type.startsWith(\"TOOL_CALL\"),\n );\n}\n\nexport function ensureRunEnvelope(\n events: ReadonlyArray<SnippetEvent>,\n ids: { threadId: string; runId: string },\n): SnippetEvent[] {\n const next = [...events];\n const hasStart = next.some((event) => event.type === \"RUN_STARTED\");\n const hasFinish = next.some((event) => event.type === \"RUN_FINISHED\");\n if (!hasStart) {\n next.unshift({\n type: \"RUN_STARTED\",\n threadId: ids.threadId,\n runId: ids.runId,\n });\n }\n if (!hasFinish) {\n next.push({\n type: \"RUN_FINISHED\",\n threadId: ids.threadId,\n runId: ids.runId,\n });\n }\n return next;\n}\n\nexport function snippetArgsJson(value: unknown): string {\n if (value && typeof value === \"object\") {\n return JSON.stringify(value);\n }\n if (typeof value !== \"string\") {\n return \"{}\";\n }\n const trimmed = value.trim() || \"{}\";\n try {\n return JSON.stringify(JSON.parse(trimmed));\n } catch {\n const recovered = firstJsonValue(trimmed);\n if (recovered !== undefined) {\n return JSON.stringify(recovered);\n }\n throw new Error(\"Tool args JSON is invalid.\");\n }\n}\n\n// ponytail: single depth scan instead of parse-every-prefix. Recovers the first\n// complete object from text with trailing junk, and bails at once on truncated\n// args (a streaming tool call) rather than walking the whole string.\nfunction firstJsonValue(raw: string): unknown {\n const start = raw.indexOf(\"{\");\n if (start === -1) {\n return undefined;\n }\n let depth = 0;\n let inString = false;\n let escaped = false;\n for (let index = start; index < raw.length; index += 1) {\n const char = raw[index];\n if (inString) {\n if (escaped) {\n escaped = false;\n } else if (char === \"\\\\\") {\n escaped = true;\n } else if (char === '\"') {\n inString = false;\n }\n continue;\n }\n if (char === '\"') {\n inString = true;\n } else if (char === \"{\") {\n depth += 1;\n } else if (char === \"}\") {\n depth -= 1;\n if (depth === 0) {\n try {\n return JSON.parse(raw.slice(start, index + 1));\n } catch {\n return undefined;\n }\n }\n }\n }\n return undefined;\n}\n\nexport function compileToolCallRecipe(input: {\n toolName: string;\n argsJson: string | Record<string, unknown>;\n threadId: string;\n runId: string;\n parentMessageId?: string;\n toolCallId?: string;\n}): SnippetEvent[] {\n const toolName = input.toolName.trim();\n if (!toolName) {\n throw new Error(\"Tool name is required.\");\n }\n const argsJson = snippetArgsJson(input.argsJson);\n parseObjectJson(argsJson, \"Tool args\");\n const parentMessageId = input.parentMessageId ?? createSnippetId();\n const toolCallId = input.toolCallId ?? createSnippetId();\n return ensureRunEnvelope(\n [\n {\n type: \"TOOL_CALL_START\",\n toolCallId,\n toolCallName: toolName,\n parentMessageId,\n },\n {\n type: \"TOOL_CALL_ARGS\",\n toolCallId,\n delta: argsJson,\n },\n {\n type: \"TOOL_CALL_END\",\n toolCallId,\n },\n ],\n input,\n );\n}\n\nexport function compileReasoningRecipe(input: {\n text: string;\n threadId: string;\n runId: string;\n}): SnippetEvent[] {\n const text = input.text.trim();\n if (!text) {\n throw new Error(\"Reasoning text is required.\");\n }\n const messageId = createSnippetId();\n return ensureRunEnvelope(\n [\n { type: \"REASONING_START\", messageId },\n { type: \"REASONING_MESSAGE_START\", messageId, role: \"reasoning\" },\n { type: \"REASONING_MESSAGE_CONTENT\", messageId, delta: text },\n { type: \"REASONING_MESSAGE_END\", messageId },\n { type: \"REASONING_END\", messageId },\n ],\n input,\n );\n}\n\nexport function compileTextRecipe(input: {\n text: string;\n threadId: string;\n runId: string;\n messageId?: string;\n}): SnippetEvent[] {\n const text = input.text.trim();\n if (!text) {\n throw new Error(\"Assistant text is required.\");\n }\n const messageId = input.messageId ?? createSnippetId();\n return ensureRunEnvelope(\n [\n { type: \"TEXT_MESSAGE_START\", messageId, role: \"assistant\" },\n { type: \"TEXT_MESSAGE_CONTENT\", messageId, delta: text },\n { type: \"TEXT_MESSAGE_END\", messageId },\n ],\n input,\n );\n}\n\nexport function compileActivityRecipe(input: {\n activityType: string;\n contentJson: string;\n threadId: string;\n runId: string;\n messageId?: string;\n}): SnippetEvent[] {\n const activityType = input.activityType.trim();\n if (!activityType) {\n throw new Error(\"Activity type is required.\");\n }\n const content = parseObjectJson(input.contentJson, \"Activity content\");\n const messageId = input.messageId ?? createSnippetId();\n return ensureRunEnvelope(\n [\n {\n type: \"ACTIVITY_SNAPSHOT\",\n messageId,\n activityType,\n content,\n replace: true,\n },\n ],\n input,\n );\n}\n\nexport type ChatSnippetCapture =\n | {\n kind: \"text\";\n messageId: string;\n content: string;\n }\n | {\n kind: \"reasoning\";\n messageId: string;\n content: string;\n }\n | {\n kind: \"tool-call\";\n messageId: string;\n toolCallId: string;\n toolName: string;\n argsJson: string | Record<string, unknown>;\n }\n | {\n kind: \"activity\";\n messageId: string;\n activityType: string;\n content: unknown;\n };\n\nexport type RecipeDraft = {\n toolName: string;\n toolArgs: string;\n reasoningText: string;\n textContent: string;\n activityType: string;\n activityContent: string;\n};\n\nexport function emptyRecipeDraft(): RecipeDraft {\n return {\n toolName: \"\",\n toolArgs: \"{}\",\n reasoningText: \"\",\n textContent: \"\",\n activityType: \"a2ui-surface\",\n activityContent: \"{}\",\n };\n}\n\nexport function compileChatSnippet(\n input: ChatSnippetCapture & { threadId: string; runId: string },\n): { recipe: SnippetRecipe; events: SnippetEvent[]; name: string } {\n switch (input.kind) {\n case \"text\":\n return {\n recipe: \"text\",\n events: compileTextRecipe({\n text: input.content,\n threadId: input.threadId,\n runId: input.runId,\n messageId: input.messageId,\n }),\n name: autoSnippetName(\"text\", snippetLabelFromText(input.content)),\n };\n case \"reasoning\":\n return {\n recipe: \"reasoning\",\n events: compileReasoningRecipe({\n text: input.content,\n threadId: input.threadId,\n runId: input.runId,\n }),\n name: autoSnippetName(\"reasoning\", snippetLabelFromText(input.content)),\n };\n case \"tool-call\":\n return {\n recipe: \"tool-call\",\n events: compileToolCallRecipe({\n toolName: input.toolName,\n argsJson: snippetArgsJson(input.argsJson),\n threadId: input.threadId,\n runId: input.runId,\n parentMessageId: input.messageId,\n toolCallId: input.toolCallId,\n }),\n name: autoSnippetName(\"tool-call\", input.toolName),\n };\n case \"activity\":\n return {\n recipe: \"activity\",\n events: compileActivityRecipe({\n activityType: input.activityType,\n contentJson: JSON.stringify(input.content ?? {}),\n threadId: input.threadId,\n runId: input.runId,\n messageId: input.messageId,\n }),\n name: autoSnippetName(\"activity\", input.activityType),\n };\n }\n}\n\nexport function editorStateFromSnippet(snippet: EventSnippet): {\n recipe: SnippetRecipe;\n name: string;\n json: string;\n draft: RecipeDraft;\n} {\n return {\n recipe: snippet.recipe,\n name: snippet.name,\n json: JSON.stringify(snippet.events, null, 2),\n draft: recipeDraftFromEvents(snippet.events),\n };\n}\n\nexport function recipeDraftFromEvents(\n events: ReadonlyArray<SnippetEvent>,\n): RecipeDraft {\n const draft = emptyRecipeDraft();\n const textParts: string[] = [];\n const reasoningParts: string[] = [];\n const argParts: string[] = [];\n\n for (const event of events) {\n if (event.type === \"TEXT_MESSAGE_CONTENT\") {\n const delta = readEventString(event, \"delta\");\n if (delta) {\n textParts.push(delta);\n }\n }\n if (event.type === \"REASONING_MESSAGE_CONTENT\") {\n const delta = readEventString(event, \"delta\");\n if (delta) {\n reasoningParts.push(delta);\n }\n }\n if (event.type === \"TOOL_CALL_START\") {\n const toolName = readEventString(event, \"toolCallName\");\n if (toolName) {\n draft.toolName = toolName;\n }\n }\n if (event.type === \"TOOL_CALL_ARGS\") {\n const delta = readEventString(event, \"delta\");\n if (delta) {\n argParts.push(delta);\n }\n }\n if (event.type === \"ACTIVITY_SNAPSHOT\") {\n const activityType = readEventString(event, \"activityType\");\n if (activityType) {\n draft.activityType = activityType;\n }\n draft.activityContent = prettyJson(event.content ?? {});\n }\n }\n\n draft.textContent = textParts.join(\"\");\n draft.reasoningText = reasoningParts.join(\"\");\n if (argParts.length > 0) {\n draft.toolArgs = prettyJson(argParts.join(\"\"));\n }\n return draft;\n}\n\nexport function compileFromActivityMessage(input: {\n message: ActivitySnippetMessage;\n threadId: string;\n runId: string;\n}): { recipe: SnippetRecipe; events: SnippetEvent[]; name: string } {\n return compileChatSnippet({\n kind: \"activity\",\n messageId: input.message.id,\n activityType: input.message.activityType,\n content: input.message.content,\n threadId: input.threadId,\n runId: input.runId,\n });\n}\n\nexport function parseSnippetEvents(raw: string): SnippetEvent[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\"Snippet JSON is invalid.\");\n }\n if (!Array.isArray(parsed)) {\n throw new Error(\"Snippet JSON must be an array of events.\");\n }\n const events: SnippetEvent[] = [];\n for (const item of parsed) {\n if (!item || typeof item !== \"object\" || Array.isArray(item)) {\n throw new Error(\"Each event must be an object with a string type.\");\n }\n const record = item as { type?: unknown };\n if (typeof record.type !== \"string\" || record.type.length === 0) {\n throw new Error(\"Each event must have a string type.\");\n }\n events.push(item as SnippetEvent);\n }\n return events;\n}\n\nexport function loadEventSnippets(\n storage: Pick<Storage, \"getItem\"> | null = defaultStorage(),\n): EventSnippet[] {\n if (!storage) {\n return [];\n }\n try {\n const raw = storage.getItem(EVENT_SNIPPETS_STORAGE_KEY);\n if (!raw) {\n return [];\n }\n const parsed = JSON.parse(raw);\n if (!Array.isArray(parsed)) {\n return [];\n }\n return parsed.flatMap((item) => {\n const snippet = parseStoredSnippet(item);\n return snippet ? [snippet] : [];\n });\n } catch {\n return [];\n }\n}\n\nexport function saveEventSnippets(\n snippets: ReadonlyArray<EventSnippet>,\n storage: Pick<Storage, \"setItem\"> | null = defaultStorage(),\n): void {\n if (!storage) {\n throw new Error(\"Snippet storage is not available.\");\n }\n storage.setItem(EVENT_SNIPPETS_STORAGE_KEY, JSON.stringify(snippets));\n}\n\nexport function upsertEventSnippet(\n snippet: EventSnippet,\n storage: Pick<Storage, \"getItem\" | \"setItem\"> | null = defaultStorage(),\n): EventSnippet[] {\n const current = loadEventSnippets(storage);\n const index = current.findIndex((item) => item.id === snippet.id);\n const next =\n index === -1\n ? [...current, snippet]\n : current.map((item, itemIndex) =>\n itemIndex === index ? snippet : item,\n );\n saveEventSnippets(next, storage);\n return next;\n}\n\nexport function deleteEventSnippet(\n id: string,\n storage: Pick<Storage, \"getItem\" | \"setItem\"> | null = defaultStorage(),\n): EventSnippet[] {\n const next = loadEventSnippets(storage).filter((item) => item.id !== id);\n saveEventSnippets(next, storage);\n return next;\n}\n\nexport function exportEventSnippetsJson(\n snippets: ReadonlyArray<EventSnippet>,\n): string {\n return JSON.stringify(snippets, null, 2);\n}\n\nexport function importEventSnippets(\n raw: string,\n storage: Pick<Storage, \"getItem\" | \"setItem\"> | null = defaultStorage(),\n): EventSnippet[] {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(\"Import JSON is invalid.\");\n }\n const incoming = Array.isArray(parsed) ? parsed : [parsed];\n const imported: EventSnippet[] = [];\n for (const item of incoming) {\n const snippet = parseStoredSnippet(item);\n if (!snippet) {\n throw new Error(\"Import JSON is not a valid snippet list.\");\n }\n imported.push({\n ...snippet,\n id: createSnippetId(),\n createdAt: new Date().toISOString(),\n updatedAt: new Date().toISOString(),\n });\n }\n if (imported.length === 0) {\n throw new Error(\"Import JSON is not a valid snippet list.\");\n }\n const next = [...loadEventSnippets(storage), ...imported];\n saveEventSnippets(next, storage);\n return next;\n}\n\nfunction snippetLabelFromText(text: string): string {\n const compact = text.trim().replace(/\\s+/g, \" \");\n if (compact.length <= 40) {\n return compact;\n }\n return `${compact.slice(0, 37)}...`;\n}\n\nfunction parseObjectJson(raw: string, label: string): unknown {\n try {\n return JSON.parse(raw);\n } catch {\n throw new Error(`${label} JSON is invalid.`);\n }\n}\n\nfunction readEventString(event: SnippetEvent, key: string): string | null {\n const value = event[key];\n return typeof value === \"string\" && value.length > 0 ? value : null;\n}\n\nfunction prettyJson(value: unknown): string {\n if (typeof value === \"string\") {\n try {\n return JSON.stringify(JSON.parse(value), null, 2);\n } catch {\n return value;\n }\n }\n try {\n return JSON.stringify(value ?? {}, null, 2);\n } catch {\n return \"{}\";\n }\n}\n\nfunction parseStoredSnippet(value: unknown): EventSnippet | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return null;\n }\n const record = value as {\n id?: unknown;\n name?: unknown;\n recipe?: unknown;\n events?: unknown;\n createdAt?: unknown;\n updatedAt?: unknown;\n };\n if (typeof record.id !== \"string\" || record.id.length === 0) {\n return null;\n }\n if (typeof record.name !== \"string\") {\n return null;\n }\n if (!isSnippetRecipe(record.recipe)) {\n return null;\n }\n if (!Array.isArray(record.events)) {\n return null;\n }\n const events: SnippetEvent[] = [];\n for (const event of record.events) {\n if (!event || typeof event !== \"object\" || Array.isArray(event)) {\n return null;\n }\n if (typeof (event as SnippetEvent).type !== \"string\") {\n return null;\n }\n events.push(event as SnippetEvent);\n }\n return {\n id: record.id,\n name: record.name,\n recipe: record.recipe,\n events,\n createdAt:\n typeof record.createdAt === \"string\"\n ? record.createdAt\n : new Date().toISOString(),\n updatedAt:\n typeof record.updatedAt === \"string\"\n ? record.updatedAt\n : new Date().toISOString(),\n };\n}\n\nfunction defaultStorage(): Pick<Storage, \"getItem\" | \"setItem\"> | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n try {\n return window.localStorage;\n } catch {\n return null;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/lib/home-briefing.ts
|
|
3
|
+
const SERVICE_DOCS_URL = {
|
|
4
|
+
threads: "https://docs.copilotkit.ai/threads",
|
|
5
|
+
memory: "https://docs.copilotkit.ai/premium/intelligence-platform",
|
|
6
|
+
a2ui: "https://docs.copilotkit.ai/generative-ui/a2ui",
|
|
7
|
+
"open-gen-ui": "https://docs.copilotkit.ai/generative-ui/open-generative-ui",
|
|
8
|
+
suggestions: "https://docs.copilotkit.ai/agentic-chat-ui",
|
|
9
|
+
audio: "https://docs.copilotkit.ai/voice",
|
|
10
|
+
websocket: "https://docs.copilotkit.ai/premium/intelligence-platform"
|
|
11
|
+
};
|
|
12
|
+
/** Return the Home hero button for a trusted metadata action. */
|
|
13
|
+
function homeHeroActionFromMetadata(action) {
|
|
14
|
+
if (action.kind === "manage_plan") return {
|
|
15
|
+
kind: action.kind,
|
|
16
|
+
url: action.url,
|
|
17
|
+
label: "Manage plan"
|
|
18
|
+
};
|
|
19
|
+
if (action.kind === "renew") return {
|
|
20
|
+
kind: action.kind,
|
|
21
|
+
url: action.url,
|
|
22
|
+
label: "Renew plan"
|
|
23
|
+
};
|
|
24
|
+
return {
|
|
25
|
+
kind: action.kind,
|
|
26
|
+
url: action.url,
|
|
27
|
+
label: "Setup Intelligence"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** Return a short preview from markdown, without links or headings. */
|
|
31
|
+
function announcementPreview(markdown, maxLength = 140) {
|
|
32
|
+
const plain = markdown.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[#*_`]/g, "").replace(/\s+/g, " ").trim();
|
|
33
|
+
if (plain.length <= maxLength) return plain;
|
|
34
|
+
return `${plain.slice(0, maxLength).trimEnd()}…`;
|
|
35
|
+
}
|
|
36
|
+
function connectIntelligenceAction(action, connectUrl, licenseState) {
|
|
37
|
+
if (action) return action;
|
|
38
|
+
if (connectUrl && (licenseState === "none" || licenseState === "unknown")) return homeHeroActionFromMetadata({
|
|
39
|
+
kind: "enable_intelligence",
|
|
40
|
+
url: connectUrl
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function heroForState(args) {
|
|
44
|
+
if (!args.connected) {
|
|
45
|
+
const renewing = args.action?.kind === "renew";
|
|
46
|
+
return {
|
|
47
|
+
connection: "disconnected",
|
|
48
|
+
title: renewing ? "Renew Intelligence" : "Intelligence is not setup",
|
|
49
|
+
body: renewing ? "Renew Intelligence to restore persistent Threads and Memory." : "Connect CopilotKit Intelligence to add persistent Threads, Learning and Analytics to your application. Inspect conversations and allow your agents to learn from real use.",
|
|
50
|
+
action: connectIntelligenceAction(args.action, args.connectUrl, args.licenseState)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
connection: "connected",
|
|
55
|
+
title: "Connected to Intelligence",
|
|
56
|
+
body: "Use Workbench to inspect threads and memory.",
|
|
57
|
+
action: args.action?.kind === "enable_intelligence" ? void 0 : args.action
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function usageFromMetadata(metadata) {
|
|
61
|
+
const usage = metadata.usage;
|
|
62
|
+
if (!usage) return;
|
|
63
|
+
if (usage.limit.kind === "finite") return {
|
|
64
|
+
used: usage.used,
|
|
65
|
+
limitLabel: `${usage.used} / ${usage.limit.value}`,
|
|
66
|
+
ratio: usage.limit.value === 0 ? 1 : usage.used / usage.limit.value
|
|
67
|
+
};
|
|
68
|
+
if (usage.limit.kind === "unlimited") return {
|
|
69
|
+
used: usage.used,
|
|
70
|
+
limitLabel: `${usage.used} / unlimited`
|
|
71
|
+
};
|
|
72
|
+
return {
|
|
73
|
+
used: usage.used,
|
|
74
|
+
limitLabel: `${usage.used} used`
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function newsFromAnnouncement(args) {
|
|
78
|
+
const markdown = args.markdown?.trim();
|
|
79
|
+
if (markdown && args.html) return {
|
|
80
|
+
title: markdown.match(/^#{1,3}\s+(.+)$/m)?.[1]?.trim() || "The latest from CopilotKit",
|
|
81
|
+
previewText: args.previewText?.trim() || announcementPreview(markdown, 160),
|
|
82
|
+
documentHtml: args.html,
|
|
83
|
+
empty: false
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
title: "You're all caught up",
|
|
87
|
+
previewText: "Latest CopilotKit updates will appear here.",
|
|
88
|
+
empty: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function runtimeEventSignal(event) {
|
|
92
|
+
if (!event) return {
|
|
93
|
+
label: "No events yet",
|
|
94
|
+
tone: "muted"
|
|
95
|
+
};
|
|
96
|
+
const eventDetails = {
|
|
97
|
+
id: event.id,
|
|
98
|
+
agentId: event.agentId,
|
|
99
|
+
type: event.type,
|
|
100
|
+
timestamp: event.timestamp
|
|
101
|
+
};
|
|
102
|
+
if (event.type === "RUN_ERROR" || event.type === "ERROR") return {
|
|
103
|
+
label: event.type === "RUN_ERROR" ? "Run error" : "Error",
|
|
104
|
+
tone: "error",
|
|
105
|
+
...eventDetails
|
|
106
|
+
};
|
|
107
|
+
if (event.type === "RUN_FINISHED") return {
|
|
108
|
+
label: "Run completed",
|
|
109
|
+
tone: "success",
|
|
110
|
+
...eventDetails
|
|
111
|
+
};
|
|
112
|
+
if (event.type.endsWith("_START") || event.type.endsWith("_STARTED") || event.type.endsWith("_CONTENT") || event.type.endsWith("_ARGS") || event.type.endsWith("_DELTA")) return {
|
|
113
|
+
label: "Last event is in progress",
|
|
114
|
+
tone: "active",
|
|
115
|
+
...eventDetails
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
label: "Last event received",
|
|
119
|
+
tone: "success",
|
|
120
|
+
...eventDetails
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Whether the runtime *connection* needs attention — the single condition
|
|
125
|
+
* shared by System Health and the launcher's error signal, so the two can
|
|
126
|
+
* never disagree about whether the wiring is broken.
|
|
127
|
+
*
|
|
128
|
+
* Exactly one state counts. `disconnected` is also the initial value, so
|
|
129
|
+
* counting it would raise the signal on every page load; `connecting` is a
|
|
130
|
+
* normal startup step; `unavailable` means no Core is attached, which is not
|
|
131
|
+
* a defect of the developer's wiring.
|
|
132
|
+
*
|
|
133
|
+
* Note the deliberate asymmetry with `health.state`: a failed *run* also
|
|
134
|
+
* drives System Health to "Needs attention" while the connection is fine.
|
|
135
|
+
* That is an event rather than a state, and it is excluded from the launcher
|
|
136
|
+
* on purpose — see the launcher-signal comments in index.ts.
|
|
137
|
+
*/
|
|
138
|
+
function runtimeConnectionNeedsAttention(state) {
|
|
139
|
+
return state === "error";
|
|
140
|
+
}
|
|
141
|
+
function runtimeHealthFromInput(input) {
|
|
142
|
+
const lastEvent = runtimeEventSignal(input.lastRuntimeEvent);
|
|
143
|
+
if (input.runtimeConnectionState === "connected") return {
|
|
144
|
+
state: lastEvent.tone === "error" ? "error" : "healthy",
|
|
145
|
+
label: lastEvent.tone === "error" ? "Needs attention" : "Healthy",
|
|
146
|
+
runtime: {
|
|
147
|
+
label: "Available",
|
|
148
|
+
tone: "success"
|
|
149
|
+
},
|
|
150
|
+
liveUpdates: {
|
|
151
|
+
label: "Ready",
|
|
152
|
+
tone: "success"
|
|
153
|
+
},
|
|
154
|
+
lastEvent
|
|
155
|
+
};
|
|
156
|
+
if (input.runtimeConnectionState === "connecting") return {
|
|
157
|
+
state: "checking",
|
|
158
|
+
label: "Checking",
|
|
159
|
+
runtime: {
|
|
160
|
+
label: "Checking",
|
|
161
|
+
tone: "active"
|
|
162
|
+
},
|
|
163
|
+
liveUpdates: {
|
|
164
|
+
label: "Connecting",
|
|
165
|
+
tone: "active"
|
|
166
|
+
},
|
|
167
|
+
lastEvent
|
|
168
|
+
};
|
|
169
|
+
if (input.runtimeConnectionState === "unavailable") return {
|
|
170
|
+
state: "unavailable",
|
|
171
|
+
label: "Unavailable",
|
|
172
|
+
runtime: {
|
|
173
|
+
label: "Unavailable",
|
|
174
|
+
tone: "muted"
|
|
175
|
+
},
|
|
176
|
+
liveUpdates: {
|
|
177
|
+
label: "Not attached",
|
|
178
|
+
tone: "muted"
|
|
179
|
+
},
|
|
180
|
+
lastEvent
|
|
181
|
+
};
|
|
182
|
+
const needsAttention = runtimeConnectionNeedsAttention(input.runtimeConnectionState);
|
|
183
|
+
return {
|
|
184
|
+
state: needsAttention ? "error" : "offline",
|
|
185
|
+
label: needsAttention ? "Runtime error" : "Offline",
|
|
186
|
+
runtime: {
|
|
187
|
+
label: "Offline",
|
|
188
|
+
tone: "error"
|
|
189
|
+
},
|
|
190
|
+
liveUpdates: {
|
|
191
|
+
label: needsAttention ? "Error" : "Disconnected",
|
|
192
|
+
tone: "error"
|
|
193
|
+
},
|
|
194
|
+
lastEvent
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/** Build the Home briefing from data the Inspector already has. */
|
|
198
|
+
function buildHomeModel(input) {
|
|
199
|
+
const metadataAction = input.metadata.threadsFooterAction ?? input.metadata.lockedAction;
|
|
200
|
+
const action = metadataAction ? homeHeroActionFromMetadata({
|
|
201
|
+
kind: metadataAction.kind,
|
|
202
|
+
url: metadataAction.url
|
|
203
|
+
}) : void 0;
|
|
204
|
+
const identity = input.metadata.identity;
|
|
205
|
+
const projectLinked = Boolean(identity);
|
|
206
|
+
const intelligenceConnected = input.metadata.licenseState === "valid" || input.metadata.licenseState === "unknown" && input.intelligenceConnected;
|
|
207
|
+
const usage = usageFromMetadata(input.metadata);
|
|
208
|
+
return {
|
|
209
|
+
hero: heroForState({
|
|
210
|
+
connected: intelligenceConnected,
|
|
211
|
+
action,
|
|
212
|
+
connectUrl: input.intelligenceSignupUrl,
|
|
213
|
+
licenseState: input.metadata.licenseState
|
|
214
|
+
}),
|
|
215
|
+
projectLinked,
|
|
216
|
+
project: identity ? {
|
|
217
|
+
organizationName: identity.organizationName,
|
|
218
|
+
projectName: identity.projectName,
|
|
219
|
+
planLabel: input.metadata.plan?.label,
|
|
220
|
+
license: input.metadata.licenseState,
|
|
221
|
+
usage
|
|
222
|
+
} : input.metadata.plan || usage ? {
|
|
223
|
+
organizationName: "Not linked",
|
|
224
|
+
projectName: "This runtime is not linked to a project",
|
|
225
|
+
planLabel: input.metadata.plan?.label,
|
|
226
|
+
license: input.metadata.licenseState,
|
|
227
|
+
usage
|
|
228
|
+
} : void 0,
|
|
229
|
+
runtime: {
|
|
230
|
+
url: input.runtimeUrl,
|
|
231
|
+
health: runtimeHealthFromInput(input)
|
|
232
|
+
},
|
|
233
|
+
services: [
|
|
234
|
+
{
|
|
235
|
+
id: "threads",
|
|
236
|
+
label: "Threads",
|
|
237
|
+
enabled: intelligenceConnected && input.threadsAvailable,
|
|
238
|
+
url: input.runtimeUrl,
|
|
239
|
+
docsUrl: SERVICE_DOCS_URL.threads
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
id: "memory",
|
|
243
|
+
label: "Memory",
|
|
244
|
+
enabled: intelligenceConnected && input.memoriesOn,
|
|
245
|
+
docsUrl: SERVICE_DOCS_URL.memory
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
id: "a2ui",
|
|
249
|
+
label: "A2UI",
|
|
250
|
+
enabled: input.a2uiOn,
|
|
251
|
+
docsUrl: SERVICE_DOCS_URL.a2ui
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: "open-gen-ui",
|
|
255
|
+
label: "Open Gen UI",
|
|
256
|
+
enabled: input.openGenUiOn,
|
|
257
|
+
docsUrl: SERVICE_DOCS_URL["open-gen-ui"]
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: "suggestions",
|
|
261
|
+
label: "Suggestions",
|
|
262
|
+
enabled: input.suggestionsOn,
|
|
263
|
+
docsUrl: SERVICE_DOCS_URL.suggestions
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: "audio",
|
|
267
|
+
label: "Audio",
|
|
268
|
+
enabled: input.audioOn,
|
|
269
|
+
docsUrl: SERVICE_DOCS_URL.audio
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
id: "websocket",
|
|
273
|
+
label: "Websocket",
|
|
274
|
+
enabled: intelligenceConnected && Boolean(input.websocketUrl),
|
|
275
|
+
url: input.websocketUrl,
|
|
276
|
+
docsUrl: SERVICE_DOCS_URL.websocket
|
|
277
|
+
}
|
|
278
|
+
],
|
|
279
|
+
news: newsFromAnnouncement({
|
|
280
|
+
previewText: input.announcementPreviewText,
|
|
281
|
+
markdown: input.announcementMarkdown,
|
|
282
|
+
html: input.announcementHtml ?? void 0
|
|
283
|
+
})
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
//#endregion
|
|
288
|
+
exports.buildHomeModel = buildHomeModel;
|
|
289
|
+
exports.runtimeConnectionNeedsAttention = runtimeConnectionNeedsAttention;
|
|
290
|
+
//# sourceMappingURL=home-briefing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"home-briefing.cjs","names":[],"sources":["../../src/lib/home-briefing.ts"],"sourcesContent":["import type { InspectorMetadataProjection } from \"./inspector-metadata.js\";\n\nexport type HomeHeroActionKind =\n | \"enable_intelligence\"\n | \"manage_plan\"\n | \"renew\";\n\nexport type HomeHeroAction = {\n kind: HomeHeroActionKind;\n url: string;\n label: string;\n};\n\nexport type HomeConnection = \"connected\" | \"disconnected\";\n\nexport type HomeRuntimeConnectionState =\n | \"connected\"\n | \"connecting\"\n | \"disconnected\"\n | \"error\"\n | \"unavailable\";\n\nexport type HomeRuntimeHealthTone = \"success\" | \"active\" | \"error\" | \"muted\";\n\nexport type HomeServiceId =\n | \"threads\"\n | \"memory\"\n | \"a2ui\"\n | \"open-gen-ui\"\n | \"suggestions\"\n | \"audio\"\n | \"websocket\";\n\nexport type HomeServiceTile = {\n id: HomeServiceId;\n label: string;\n enabled: boolean;\n url?: string;\n docsUrl: string;\n};\n\nexport type HomeModel = {\n hero: {\n connection: HomeConnection;\n title: string;\n body: string;\n action?: HomeHeroAction;\n };\n project?: {\n organizationName: string;\n projectName: string;\n planLabel?: string;\n license: string;\n usage?: {\n used: number;\n limitLabel: string;\n ratio?: number;\n };\n };\n projectLinked: boolean;\n runtime: {\n url?: string;\n health: {\n state: \"healthy\" | \"checking\" | \"offline\" | \"error\" | \"unavailable\";\n label: string;\n runtime: {\n label: string;\n tone: HomeRuntimeHealthTone;\n };\n liveUpdates: {\n label: string;\n tone: HomeRuntimeHealthTone;\n };\n lastEvent: {\n label: string;\n tone: HomeRuntimeHealthTone;\n id?: string;\n agentId?: string;\n type?: string;\n timestamp?: number;\n };\n };\n };\n services: HomeServiceTile[];\n news: {\n title: string;\n previewText: string;\n documentHtml?: string;\n empty: boolean;\n };\n};\n\nexport type HomeBriefingInput = {\n intelligenceConnected: boolean;\n threadsAvailable: boolean;\n metadata: InspectorMetadataProjection;\n runtimeUrl?: string;\n runtimeConnectionState: HomeRuntimeConnectionState;\n lastRuntimeEvent?: {\n id: string;\n agentId: string;\n type: string;\n timestamp: number;\n };\n memoriesOn: boolean;\n a2uiOn: boolean;\n openGenUiOn: boolean;\n suggestionsOn: boolean;\n audioOn: boolean;\n websocketUrl?: string;\n announcementPreviewText?: string;\n announcementMarkdown?: string;\n announcementHtml?: string;\n intelligenceSignupUrl?: string;\n};\n\nconst SERVICE_DOCS_URL: Record<HomeServiceId, string> = {\n threads: \"https://docs.copilotkit.ai/threads\",\n memory: \"https://docs.copilotkit.ai/premium/intelligence-platform\",\n a2ui: \"https://docs.copilotkit.ai/generative-ui/a2ui\",\n \"open-gen-ui\": \"https://docs.copilotkit.ai/generative-ui/open-generative-ui\",\n suggestions: \"https://docs.copilotkit.ai/agentic-chat-ui\",\n audio: \"https://docs.copilotkit.ai/voice\",\n websocket: \"https://docs.copilotkit.ai/premium/intelligence-platform\",\n};\n\n/** Return the Home hero button for a trusted metadata action. */\nexport function homeHeroActionFromMetadata(action: {\n kind: HomeHeroActionKind;\n url: string;\n}): HomeHeroAction {\n if (action.kind === \"manage_plan\") {\n return { kind: action.kind, url: action.url, label: \"Manage plan\" };\n }\n\n if (action.kind === \"renew\") {\n return { kind: action.kind, url: action.url, label: \"Renew plan\" };\n }\n\n return {\n kind: action.kind,\n url: action.url,\n label: \"Setup Intelligence\",\n };\n}\n\n/** Return a short preview from markdown, without links or headings. */\nexport function announcementPreview(markdown: string, maxLength = 140): string {\n const plain = markdown\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, \"$1\")\n .replace(/[#*_`]/g, \"\")\n .replace(/\\s+/g, \" \")\n .trim();\n if (plain.length <= maxLength) {\n return plain;\n }\n\n return `${plain.slice(0, maxLength).trimEnd()}…`;\n}\n\nfunction connectIntelligenceAction(\n action: HomeHeroAction | undefined,\n connectUrl?: string,\n licenseState?: InspectorMetadataProjection[\"licenseState\"],\n): HomeHeroAction | undefined {\n if (action) {\n return action;\n }\n\n if (connectUrl && (licenseState === \"none\" || licenseState === \"unknown\")) {\n return homeHeroActionFromMetadata({\n kind: \"enable_intelligence\",\n url: connectUrl,\n });\n }\n\n return undefined;\n}\n\nfunction heroForState(args: {\n connected: boolean;\n action?: HomeHeroAction;\n connectUrl?: string;\n licenseState: InspectorMetadataProjection[\"licenseState\"];\n}): HomeModel[\"hero\"] {\n if (!args.connected) {\n const renewing = args.action?.kind === \"renew\";\n return {\n connection: \"disconnected\",\n title: renewing ? \"Renew Intelligence\" : \"Intelligence is not setup\",\n body: renewing\n ? \"Renew Intelligence to restore persistent Threads and Memory.\"\n : \"Connect CopilotKit Intelligence to add persistent Threads, Learning and Analytics to your application. Inspect conversations and allow your agents to learn from real use.\",\n action: connectIntelligenceAction(\n args.action,\n args.connectUrl,\n args.licenseState,\n ),\n };\n }\n\n return {\n connection: \"connected\",\n title: \"Connected to Intelligence\",\n body: \"Use Workbench to inspect threads and memory.\",\n action:\n args.action?.kind === \"enable_intelligence\" ? undefined : args.action,\n };\n}\n\nfunction usageFromMetadata(metadata: InspectorMetadataProjection):\n | {\n used: number;\n limitLabel: string;\n ratio?: number;\n }\n | undefined {\n const usage = metadata.usage;\n if (!usage) {\n return undefined;\n }\n\n if (usage.limit.kind === \"finite\") {\n return {\n used: usage.used,\n limitLabel: `${usage.used} / ${usage.limit.value}`,\n ratio: usage.limit.value === 0 ? 1 : usage.used / usage.limit.value,\n };\n }\n\n if (usage.limit.kind === \"unlimited\") {\n return {\n used: usage.used,\n limitLabel: `${usage.used} / unlimited`,\n };\n }\n\n return {\n used: usage.used,\n limitLabel: `${usage.used} used`,\n };\n}\n\nfunction newsFromAnnouncement(args: {\n previewText?: string;\n markdown?: string;\n html?: string;\n}): HomeModel[\"news\"] {\n const markdown = args.markdown?.trim();\n if (markdown && args.html) {\n const heading = markdown.match(/^#{1,3}\\s+(.+)$/m)?.[1]?.trim();\n return {\n title: heading || \"The latest from CopilotKit\",\n previewText:\n args.previewText?.trim() || announcementPreview(markdown, 160),\n documentHtml: args.html,\n empty: false,\n };\n }\n\n return {\n title: \"You're all caught up\",\n previewText: \"Latest CopilotKit updates will appear here.\",\n empty: true,\n };\n}\n\nfunction runtimeEventSignal(\n event: HomeBriefingInput[\"lastRuntimeEvent\"],\n): HomeModel[\"runtime\"][\"health\"][\"lastEvent\"] {\n if (!event) {\n return { label: \"No events yet\", tone: \"muted\" };\n }\n\n const eventDetails = {\n id: event.id,\n agentId: event.agentId,\n type: event.type,\n timestamp: event.timestamp,\n };\n\n if (event.type === \"RUN_ERROR\" || event.type === \"ERROR\") {\n return {\n label: event.type === \"RUN_ERROR\" ? \"Run error\" : \"Error\",\n tone: \"error\",\n ...eventDetails,\n };\n }\n\n if (event.type === \"RUN_FINISHED\") {\n return {\n label: \"Run completed\",\n tone: \"success\",\n ...eventDetails,\n };\n }\n\n if (\n event.type.endsWith(\"_START\") ||\n event.type.endsWith(\"_STARTED\") ||\n event.type.endsWith(\"_CONTENT\") ||\n event.type.endsWith(\"_ARGS\") ||\n event.type.endsWith(\"_DELTA\")\n ) {\n return {\n label: \"Last event is in progress\",\n tone: \"active\",\n ...eventDetails,\n };\n }\n\n return {\n label: \"Last event received\",\n tone: \"success\",\n ...eventDetails,\n };\n}\n\n/**\n * Whether the runtime *connection* needs attention — the single condition\n * shared by System Health and the launcher's error signal, so the two can\n * never disagree about whether the wiring is broken.\n *\n * Exactly one state counts. `disconnected` is also the initial value, so\n * counting it would raise the signal on every page load; `connecting` is a\n * normal startup step; `unavailable` means no Core is attached, which is not\n * a defect of the developer's wiring.\n *\n * Note the deliberate asymmetry with `health.state`: a failed *run* also\n * drives System Health to \"Needs attention\" while the connection is fine.\n * That is an event rather than a state, and it is excluded from the launcher\n * on purpose — see the launcher-signal comments in index.ts.\n */\nexport function runtimeConnectionNeedsAttention(\n state: HomeRuntimeConnectionState,\n): boolean {\n return state === \"error\";\n}\n\nfunction runtimeHealthFromInput(\n input: HomeBriefingInput,\n): HomeModel[\"runtime\"][\"health\"] {\n const lastEvent = runtimeEventSignal(input.lastRuntimeEvent);\n\n if (input.runtimeConnectionState === \"connected\") {\n return {\n state: lastEvent.tone === \"error\" ? \"error\" : \"healthy\",\n label: lastEvent.tone === \"error\" ? \"Needs attention\" : \"Healthy\",\n runtime: { label: \"Available\", tone: \"success\" },\n liveUpdates: { label: \"Ready\", tone: \"success\" },\n lastEvent,\n };\n }\n\n if (input.runtimeConnectionState === \"connecting\") {\n return {\n state: \"checking\",\n label: \"Checking\",\n runtime: { label: \"Checking\", tone: \"active\" },\n liveUpdates: { label: \"Connecting\", tone: \"active\" },\n lastEvent,\n };\n }\n\n if (input.runtimeConnectionState === \"unavailable\") {\n return {\n state: \"unavailable\",\n label: \"Unavailable\",\n runtime: { label: \"Unavailable\", tone: \"muted\" },\n liveUpdates: { label: \"Not attached\", tone: \"muted\" },\n lastEvent,\n };\n }\n\n const needsAttention = runtimeConnectionNeedsAttention(\n input.runtimeConnectionState,\n );\n return {\n state: needsAttention ? \"error\" : \"offline\",\n label: needsAttention ? \"Runtime error\" : \"Offline\",\n runtime: { label: \"Offline\", tone: \"error\" },\n liveUpdates: {\n label: needsAttention ? \"Error\" : \"Disconnected\",\n tone: \"error\",\n },\n lastEvent,\n };\n}\n\n/** Build the Home briefing from data the Inspector already has. */\nexport function buildHomeModel(input: HomeBriefingInput): HomeModel {\n const metadataAction =\n input.metadata.threadsFooterAction ?? input.metadata.lockedAction;\n const action = metadataAction\n ? homeHeroActionFromMetadata({\n kind: metadataAction.kind,\n url: metadataAction.url,\n })\n : undefined;\n\n const identity = input.metadata.identity;\n const projectLinked = Boolean(identity);\n const intelligenceConnected =\n input.metadata.licenseState === \"valid\" ||\n (input.metadata.licenseState === \"unknown\" && input.intelligenceConnected);\n const usage = usageFromMetadata(input.metadata);\n\n return {\n hero: heroForState({\n connected: intelligenceConnected,\n action,\n connectUrl: input.intelligenceSignupUrl,\n licenseState: input.metadata.licenseState,\n }),\n projectLinked,\n project: identity\n ? {\n organizationName: identity.organizationName,\n projectName: identity.projectName,\n planLabel: input.metadata.plan?.label,\n license: input.metadata.licenseState,\n usage,\n }\n : input.metadata.plan || usage\n ? {\n organizationName: \"Not linked\",\n projectName: \"This runtime is not linked to a project\",\n planLabel: input.metadata.plan?.label,\n license: input.metadata.licenseState,\n usage,\n }\n : undefined,\n runtime: {\n url: input.runtimeUrl,\n health: runtimeHealthFromInput(input),\n },\n services: [\n {\n id: \"threads\",\n label: \"Threads\",\n enabled: intelligenceConnected && input.threadsAvailable,\n url: input.runtimeUrl,\n docsUrl: SERVICE_DOCS_URL.threads,\n },\n {\n id: \"memory\",\n label: \"Memory\",\n enabled: intelligenceConnected && input.memoriesOn,\n docsUrl: SERVICE_DOCS_URL.memory,\n },\n {\n id: \"a2ui\",\n label: \"A2UI\",\n enabled: input.a2uiOn,\n docsUrl: SERVICE_DOCS_URL.a2ui,\n },\n {\n id: \"open-gen-ui\",\n label: \"Open Gen UI\",\n enabled: input.openGenUiOn,\n docsUrl: SERVICE_DOCS_URL[\"open-gen-ui\"],\n },\n {\n id: \"suggestions\",\n label: \"Suggestions\",\n enabled: input.suggestionsOn,\n docsUrl: SERVICE_DOCS_URL.suggestions,\n },\n {\n id: \"audio\",\n label: \"Audio\",\n enabled: input.audioOn,\n docsUrl: SERVICE_DOCS_URL.audio,\n },\n {\n id: \"websocket\",\n label: \"Websocket\",\n enabled: intelligenceConnected && Boolean(input.websocketUrl),\n url: input.websocketUrl,\n docsUrl: SERVICE_DOCS_URL.websocket,\n },\n ],\n news: newsFromAnnouncement({\n previewText: input.announcementPreviewText,\n markdown: input.announcementMarkdown,\n html: input.announcementHtml ?? undefined,\n }),\n };\n}\n"],"mappings":"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"}
|