@copilotkit/web-inspector 1.69.0 → 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.
Files changed (43) hide show
  1. package/dist/index.cjs +2418 -152
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.d.cts +372 -6
  4. package/dist/index.d.cts.map +1 -1
  5. package/dist/index.d.mts +372 -6
  6. package/dist/index.d.mts.map +1 -1
  7. package/dist/index.mjs +2419 -156
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/index.umd.js +3134 -236
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/lib/event-snippets.cjs +559 -0
  12. package/dist/lib/event-snippets.cjs.map +1 -0
  13. package/dist/lib/event-snippets.d.cts +61 -0
  14. package/dist/lib/event-snippets.d.cts.map +1 -0
  15. package/dist/lib/event-snippets.d.mts +61 -0
  16. package/dist/lib/event-snippets.d.mts.map +1 -0
  17. package/dist/lib/event-snippets.mjs +537 -0
  18. package/dist/lib/event-snippets.mjs.map +1 -0
  19. package/dist/lib/home-briefing.cjs +23 -3
  20. package/dist/lib/home-briefing.cjs.map +1 -1
  21. package/dist/lib/home-briefing.mjs +23 -4
  22. package/dist/lib/home-briefing.mjs.map +1 -1
  23. package/dist/lib/inspector-nav.cjs +1 -0
  24. package/dist/lib/inspector-nav.cjs.map +1 -1
  25. package/dist/lib/inspector-nav.d.cts +11 -0
  26. package/dist/lib/inspector-nav.d.cts.map +1 -0
  27. package/dist/lib/inspector-nav.d.mts +11 -0
  28. package/dist/lib/inspector-nav.d.mts.map +1 -0
  29. package/dist/lib/inspector-nav.mjs +1 -0
  30. package/dist/lib/inspector-nav.mjs.map +1 -1
  31. package/dist/lib/telemetry.cjs +46 -1
  32. package/dist/lib/telemetry.cjs.map +1 -1
  33. package/dist/lib/telemetry.d.cts.map +1 -1
  34. package/dist/lib/telemetry.d.mts.map +1 -1
  35. package/dist/lib/telemetry.mjs +44 -2
  36. package/dist/lib/telemetry.mjs.map +1 -1
  37. package/dist/package.cjs +1 -1
  38. package/dist/package.mjs +1 -1
  39. package/dist/styles/generated.cjs +1 -1
  40. package/dist/styles/generated.cjs.map +1 -1
  41. package/dist/styles/generated.mjs +1 -1
  42. package/dist/styles/generated.mjs.map +1 -1
  43. package/package.json +3 -3
@@ -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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"}
@@ -120,6 +120,24 @@ function runtimeEventSignal(event) {
120
120
  ...eventDetails
121
121
  };
122
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
+ }
123
141
  function runtimeHealthFromInput(input) {
124
142
  const lastEvent = runtimeEventSignal(input.lastRuntimeEvent);
125
143
  if (input.runtimeConnectionState === "connected") return {
@@ -161,15 +179,16 @@ function runtimeHealthFromInput(input) {
161
179
  },
162
180
  lastEvent
163
181
  };
182
+ const needsAttention = runtimeConnectionNeedsAttention(input.runtimeConnectionState);
164
183
  return {
165
- state: input.runtimeConnectionState === "error" ? "error" : "offline",
166
- label: input.runtimeConnectionState === "error" ? "Runtime error" : "Offline",
184
+ state: needsAttention ? "error" : "offline",
185
+ label: needsAttention ? "Runtime error" : "Offline",
167
186
  runtime: {
168
187
  label: "Offline",
169
188
  tone: "error"
170
189
  },
171
190
  liveUpdates: {
172
- label: input.runtimeConnectionState === "error" ? "Error" : "Disconnected",
191
+ label: needsAttention ? "Error" : "Disconnected",
173
192
  tone: "error"
174
193
  },
175
194
  lastEvent
@@ -267,4 +286,5 @@ function buildHomeModel(input) {
267
286
 
268
287
  //#endregion
269
288
  exports.buildHomeModel = buildHomeModel;
289
+ exports.runtimeConnectionNeedsAttention = runtimeConnectionNeedsAttention;
270
290
  //# sourceMappingURL=home-briefing.cjs.map
@@ -1 +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\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 return {\n state: input.runtimeConnectionState === \"error\" ? \"error\" : \"offline\",\n label:\n input.runtimeConnectionState === \"error\" ? \"Runtime error\" : \"Offline\",\n runtime: { label: \"Offline\", tone: \"error\" },\n liveUpdates: {\n label:\n input.runtimeConnectionState === \"error\" ? \"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":";;AAoHA,MAAM,mBAAkD;CACtD,SAAS;CACT,QAAQ;CACR,MAAM;CACN,eAAe;CACf,aAAa;CACb,OAAO;CACP,WAAW;CACZ;;AAGD,SAAgB,2BAA2B,QAGxB;AACjB,KAAI,OAAO,SAAS,cAClB,QAAO;EAAE,MAAM,OAAO;EAAM,KAAK,OAAO;EAAK,OAAO;EAAe;AAGrE,KAAI,OAAO,SAAS,QAClB,QAAO;EAAE,MAAM,OAAO;EAAM,KAAK,OAAO;EAAK,OAAO;EAAc;AAGpE,QAAO;EACL,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,OAAO;EACR;;;AAIH,SAAgB,oBAAoB,UAAkB,YAAY,KAAa;CAC7E,MAAM,QAAQ,SACX,QAAQ,0BAA0B,KAAK,CACvC,QAAQ,WAAW,GAAG,CACtB,QAAQ,QAAQ,IAAI,CACpB,MAAM;AACT,KAAI,MAAM,UAAU,UAClB,QAAO;AAGT,QAAO,GAAG,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,CAAC;;AAGhD,SAAS,0BACP,QACA,YACA,cAC4B;AAC5B,KAAI,OACF,QAAO;AAGT,KAAI,eAAe,iBAAiB,UAAU,iBAAiB,WAC7D,QAAO,2BAA2B;EAChC,MAAM;EACN,KAAK;EACN,CAAC;;AAMN,SAAS,aAAa,MAKA;AACpB,KAAI,CAAC,KAAK,WAAW;EACnB,MAAM,WAAW,KAAK,QAAQ,SAAS;AACvC,SAAO;GACL,YAAY;GACZ,OAAO,WAAW,uBAAuB;GACzC,MAAM,WACF,iEACA;GACJ,QAAQ,0BACN,KAAK,QACL,KAAK,YACL,KAAK,aACN;GACF;;AAGH,QAAO;EACL,YAAY;EACZ,OAAO;EACP,MAAM;EACN,QACE,KAAK,QAAQ,SAAS,wBAAwB,SAAY,KAAK;EAClE;;AAGH,SAAS,kBAAkB,UAMb;CACZ,MAAM,QAAQ,SAAS;AACvB,KAAI,CAAC,MACH;AAGF,KAAI,MAAM,MAAM,SAAS,SACvB,QAAO;EACL,MAAM,MAAM;EACZ,YAAY,GAAG,MAAM,KAAK,KAAK,MAAM,MAAM;EAC3C,OAAO,MAAM,MAAM,UAAU,IAAI,IAAI,MAAM,OAAO,MAAM,MAAM;EAC/D;AAGH,KAAI,MAAM,MAAM,SAAS,YACvB,QAAO;EACL,MAAM,MAAM;EACZ,YAAY,GAAG,MAAM,KAAK;EAC3B;AAGH,QAAO;EACL,MAAM,MAAM;EACZ,YAAY,GAAG,MAAM,KAAK;EAC3B;;AAGH,SAAS,qBAAqB,MAIR;CACpB,MAAM,WAAW,KAAK,UAAU,MAAM;AACtC,KAAI,YAAY,KAAK,KAEnB,QAAO;EACL,OAFc,SAAS,MAAM,mBAAmB,GAAG,IAAI,MAAM,IAE3C;EAClB,aACE,KAAK,aAAa,MAAM,IAAI,oBAAoB,UAAU,IAAI;EAChE,cAAc,KAAK;EACnB,OAAO;EACR;AAGH,QAAO;EACL,OAAO;EACP,aAAa;EACb,OAAO;EACR;;AAGH,SAAS,mBACP,OAC6C;AAC7C,KAAI,CAAC,MACH,QAAO;EAAE,OAAO;EAAiB,MAAM;EAAS;CAGlD,MAAM,eAAe;EACnB,IAAI,MAAM;EACV,SAAS,MAAM;EACf,MAAM,MAAM;EACZ,WAAW,MAAM;EAClB;AAED,KAAI,MAAM,SAAS,eAAe,MAAM,SAAS,QAC/C,QAAO;EACL,OAAO,MAAM,SAAS,cAAc,cAAc;EAClD,MAAM;EACN,GAAG;EACJ;AAGH,KAAI,MAAM,SAAS,eACjB,QAAO;EACL,OAAO;EACP,MAAM;EACN,GAAG;EACJ;AAGH,KACE,MAAM,KAAK,SAAS,SAAS,IAC7B,MAAM,KAAK,SAAS,WAAW,IAC/B,MAAM,KAAK,SAAS,WAAW,IAC/B,MAAM,KAAK,SAAS,QAAQ,IAC5B,MAAM,KAAK,SAAS,SAAS,CAE7B,QAAO;EACL,OAAO;EACP,MAAM;EACN,GAAG;EACJ;AAGH,QAAO;EACL,OAAO;EACP,MAAM;EACN,GAAG;EACJ;;AAGH,SAAS,uBACP,OACgC;CAChC,MAAM,YAAY,mBAAmB,MAAM,iBAAiB;AAE5D,KAAI,MAAM,2BAA2B,YACnC,QAAO;EACL,OAAO,UAAU,SAAS,UAAU,UAAU;EAC9C,OAAO,UAAU,SAAS,UAAU,oBAAoB;EACxD,SAAS;GAAE,OAAO;GAAa,MAAM;GAAW;EAChD,aAAa;GAAE,OAAO;GAAS,MAAM;GAAW;EAChD;EACD;AAGH,KAAI,MAAM,2BAA2B,aACnC,QAAO;EACL,OAAO;EACP,OAAO;EACP,SAAS;GAAE,OAAO;GAAY,MAAM;GAAU;EAC9C,aAAa;GAAE,OAAO;GAAc,MAAM;GAAU;EACpD;EACD;AAGH,KAAI,MAAM,2BAA2B,cACnC,QAAO;EACL,OAAO;EACP,OAAO;EACP,SAAS;GAAE,OAAO;GAAe,MAAM;GAAS;EAChD,aAAa;GAAE,OAAO;GAAgB,MAAM;GAAS;EACrD;EACD;AAGH,QAAO;EACL,OAAO,MAAM,2BAA2B,UAAU,UAAU;EAC5D,OACE,MAAM,2BAA2B,UAAU,kBAAkB;EAC/D,SAAS;GAAE,OAAO;GAAW,MAAM;GAAS;EAC5C,aAAa;GACX,OACE,MAAM,2BAA2B,UAAU,UAAU;GACvD,MAAM;GACP;EACD;EACD;;;AAIH,SAAgB,eAAe,OAAqC;CAClE,MAAM,iBACJ,MAAM,SAAS,uBAAuB,MAAM,SAAS;CACvD,MAAM,SAAS,iBACX,2BAA2B;EACzB,MAAM,eAAe;EACrB,KAAK,eAAe;EACrB,CAAC,GACF;CAEJ,MAAM,WAAW,MAAM,SAAS;CAChC,MAAM,gBAAgB,QAAQ,SAAS;CACvC,MAAM,wBACJ,MAAM,SAAS,iBAAiB,WAC/B,MAAM,SAAS,iBAAiB,aAAa,MAAM;CACtD,MAAM,QAAQ,kBAAkB,MAAM,SAAS;AAE/C,QAAO;EACL,MAAM,aAAa;GACjB,WAAW;GACX;GACA,YAAY,MAAM;GAClB,cAAc,MAAM,SAAS;GAC9B,CAAC;EACF;EACA,SAAS,WACL;GACE,kBAAkB,SAAS;GAC3B,aAAa,SAAS;GACtB,WAAW,MAAM,SAAS,MAAM;GAChC,SAAS,MAAM,SAAS;GACxB;GACD,GACD,MAAM,SAAS,QAAQ,QACrB;GACE,kBAAkB;GAClB,aAAa;GACb,WAAW,MAAM,SAAS,MAAM;GAChC,SAAS,MAAM,SAAS;GACxB;GACD,GACD;EACN,SAAS;GACP,KAAK,MAAM;GACX,QAAQ,uBAAuB,MAAM;GACtC;EACD,UAAU;GACR;IACE,IAAI;IACJ,OAAO;IACP,SAAS,yBAAyB,MAAM;IACxC,KAAK,MAAM;IACX,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,yBAAyB,MAAM;IACxC,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,MAAM;IACf,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,MAAM;IACf,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,MAAM;IACf,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,MAAM;IACf,SAAS,iBAAiB;IAC3B;GACD;IACE,IAAI;IACJ,OAAO;IACP,SAAS,yBAAyB,QAAQ,MAAM,aAAa;IAC7D,KAAK,MAAM;IACX,SAAS,iBAAiB;IAC3B;GACF;EACD,MAAM,qBAAqB;GACzB,aAAa,MAAM;GACnB,UAAU,MAAM;GAChB,MAAM,MAAM,oBAAoB;GACjC,CAAC;EACH"}
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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"}
@@ -119,6 +119,24 @@ function runtimeEventSignal(event) {
119
119
  ...eventDetails
120
120
  };
121
121
  }
122
+ /**
123
+ * Whether the runtime *connection* needs attention — the single condition
124
+ * shared by System Health and the launcher's error signal, so the two can
125
+ * never disagree about whether the wiring is broken.
126
+ *
127
+ * Exactly one state counts. `disconnected` is also the initial value, so
128
+ * counting it would raise the signal on every page load; `connecting` is a
129
+ * normal startup step; `unavailable` means no Core is attached, which is not
130
+ * a defect of the developer's wiring.
131
+ *
132
+ * Note the deliberate asymmetry with `health.state`: a failed *run* also
133
+ * drives System Health to "Needs attention" while the connection is fine.
134
+ * That is an event rather than a state, and it is excluded from the launcher
135
+ * on purpose — see the launcher-signal comments in index.ts.
136
+ */
137
+ function runtimeConnectionNeedsAttention(state) {
138
+ return state === "error";
139
+ }
122
140
  function runtimeHealthFromInput(input) {
123
141
  const lastEvent = runtimeEventSignal(input.lastRuntimeEvent);
124
142
  if (input.runtimeConnectionState === "connected") return {
@@ -160,15 +178,16 @@ function runtimeHealthFromInput(input) {
160
178
  },
161
179
  lastEvent
162
180
  };
181
+ const needsAttention = runtimeConnectionNeedsAttention(input.runtimeConnectionState);
163
182
  return {
164
- state: input.runtimeConnectionState === "error" ? "error" : "offline",
165
- label: input.runtimeConnectionState === "error" ? "Runtime error" : "Offline",
183
+ state: needsAttention ? "error" : "offline",
184
+ label: needsAttention ? "Runtime error" : "Offline",
166
185
  runtime: {
167
186
  label: "Offline",
168
187
  tone: "error"
169
188
  },
170
189
  liveUpdates: {
171
- label: input.runtimeConnectionState === "error" ? "Error" : "Disconnected",
190
+ label: needsAttention ? "Error" : "Disconnected",
172
191
  tone: "error"
173
192
  },
174
193
  lastEvent
@@ -265,5 +284,5 @@ function buildHomeModel(input) {
265
284
  }
266
285
 
267
286
  //#endregion
268
- export { buildHomeModel };
287
+ export { buildHomeModel, runtimeConnectionNeedsAttention };
269
288
  //# sourceMappingURL=home-briefing.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"home-briefing.mjs","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\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 return {\n state: input.runtimeConnectionState === \"error\" ? \"error\" : \"offline\",\n label:\n input.runtimeConnectionState === \"error\" ? \"Runtime error\" : \"Offline\",\n runtime: { label: \"Offline\", tone: \"error\" },\n liveUpdates: {\n label:\n input.runtimeConnectionState === \"error\" ? \"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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"}
1
+ {"version":3,"file":"home-briefing.mjs","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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"}
@@ -10,6 +10,7 @@ const INSPECTOR_GROUPS = {
10
10
  inspect: [
11
11
  "agents",
12
12
  "ag-ui-events",
13
+ "event-snippets",
13
14
  "frontend-tools",
14
15
  "capabilities",
15
16
  "agent-context"
@@ -1 +1 @@
1
- {"version":3,"file":"inspector-nav.cjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";;AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
1
+ {"version":3,"file":"inspector-nav.cjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"event-snippets\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";;AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
@@ -0,0 +1,11 @@
1
+ //#region src/lib/inspector-nav.d.ts
2
+ declare const INSPECTOR_GROUPS: {
3
+ readonly home: readonly ["home", "whats-new"];
4
+ readonly workbench: readonly ["playground", "threads", "memories"];
5
+ readonly inspect: readonly ["agents", "ag-ui-events", "event-snippets", "frontend-tools", "capabilities", "agent-context"];
6
+ };
7
+ type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;
8
+ type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];
9
+ //#endregion
10
+ export { MenuKey };
11
+ //# sourceMappingURL=inspector-nav.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspector-nav.d.cts","names":[],"sources":["../../src/lib/inspector-nav.ts"],"mappings":";cAIa,gBAAA;EAAA;;;;KAaD,oBAAA,gBAAoC,gBAAA;AAAA,KACpC,OAAA,WAAkB,gBAAA,EAAkB,oBAAA"}
@@ -0,0 +1,11 @@
1
+ //#region src/lib/inspector-nav.d.ts
2
+ declare const INSPECTOR_GROUPS: {
3
+ readonly home: readonly ["home", "whats-new"];
4
+ readonly workbench: readonly ["playground", "threads", "memories"];
5
+ readonly inspect: readonly ["agents", "ag-ui-events", "event-snippets", "frontend-tools", "capabilities", "agent-context"];
6
+ };
7
+ type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;
8
+ type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];
9
+ //#endregion
10
+ export { MenuKey };
11
+ //# sourceMappingURL=inspector-nav.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inspector-nav.d.mts","names":[],"sources":["../../src/lib/inspector-nav.ts"],"mappings":";cAIa,gBAAA;EAAA;;;;KAaD,oBAAA,gBAAoC,gBAAA;AAAA,KACpC,OAAA,WAAkB,gBAAA,EAAkB,oBAAA"}
@@ -9,6 +9,7 @@ const INSPECTOR_GROUPS = {
9
9
  inspect: [
10
10
  "agents",
11
11
  "ag-ui-events",
12
+ "event-snippets",
12
13
  "frontend-tools",
13
14
  "capabilities",
14
15
  "agent-context"
@@ -1 +1 @@
1
- {"version":3,"file":"inspector-nav.mjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
1
+ {"version":3,"file":"inspector-nav.mjs","names":[],"sources":["../../src/lib/inspector-nav.ts"],"sourcesContent":["import type { InspectorLeafKey } from \"./telemetry.js\";\n\nexport type ShellGroupKey = \"home\" | \"workbench\" | \"inspect\";\n\nexport const INSPECTOR_GROUPS = {\n home: [\"home\", \"whats-new\"],\n workbench: [\"playground\", \"threads\", \"memories\"],\n inspect: [\n \"agents\",\n \"ag-ui-events\",\n \"event-snippets\",\n \"frontend-tools\",\n \"capabilities\",\n \"agent-context\",\n ],\n} as const satisfies Record<ShellGroupKey, readonly InspectorLeafKey[]>;\n\nexport type InspectorNavGroupKey = keyof typeof INSPECTOR_GROUPS;\nexport type MenuKey = (typeof INSPECTOR_GROUPS)[InspectorNavGroupKey][number];\n\nexport const INSPECTOR_MENU_KEYS: ReadonlyArray<MenuKey> = [\n ...INSPECTOR_GROUPS.home,\n ...INSPECTOR_GROUPS.workbench,\n ...INSPECTOR_GROUPS.inspect,\n];\n\nexport const INSPECTOR_NAV_SECTIONS: ReadonlyArray<{\n group: InspectorNavGroupKey;\n label: string | null;\n}> = [\n { group: \"home\", label: null },\n { group: \"workbench\", label: \"Workbench\" },\n { group: \"inspect\", label: \"Inspect\" },\n];\n\nexport const ICON_RAIL_MAX_WIDTH_PX = 720;\n\n/** Return whether persisted state names a known Inspector leaf. */\nexport function isInspectorMenuKey(value: unknown): value is MenuKey {\n return (\n typeof value === \"string\" &&\n INSPECTOR_MENU_KEYS.some((menuKey) => menuKey === value)\n );\n}\n\n/** Return the sidebar group that owns a leaf. */\nexport function getGroupForMenu(key: MenuKey): InspectorNavGroupKey {\n for (const group of Object.keys(INSPECTOR_GROUPS) as InspectorNavGroupKey[]) {\n if (INSPECTOR_GROUPS[group].some((menuKey) => menuKey === key)) {\n return group;\n }\n }\n\n return \"workbench\";\n}\n\n/** Return whether the sidebar should collapse to an icon rail. */\nexport function shouldUseIconRail(args: {\n dockedLeft: boolean;\n width: number;\n}): boolean {\n return args.dockedLeft || args.width < ICON_RAIL_MAX_WIDTH_PX;\n}\n"],"mappings":";AAIA,MAAa,mBAAmB;CAC9B,MAAM,CAAC,QAAQ,YAAY;CAC3B,WAAW;EAAC;EAAc;EAAW;EAAW;CAChD,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;EACD;CACF;AAKD,MAAa,sBAA8C;CACzD,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACpB,GAAG,iBAAiB;CACrB;AAED,MAAa,yBAGR;CACH;EAAE,OAAO;EAAQ,OAAO;EAAM;CAC9B;EAAE,OAAO;EAAa,OAAO;EAAa;CAC1C;EAAE,OAAO;EAAW,OAAO;EAAW;CACvC;AAED,MAAa,yBAAyB;;AAGtC,SAAgB,mBAAmB,OAAkC;AACnE,QACE,OAAO,UAAU,YACjB,oBAAoB,MAAM,YAAY,YAAY,MAAM;;;AAK5D,SAAgB,gBAAgB,KAAoC;AAClE,MAAK,MAAM,SAAS,OAAO,KAAK,iBAAiB,CAC/C,KAAI,iBAAiB,OAAO,MAAM,YAAY,YAAY,IAAI,CAC5D,QAAO;AAIX,QAAO;;;AAIT,SAAgB,kBAAkB,MAGtB;AACV,QAAO,KAAK,cAAc,KAAK,QAAQ"}
@@ -6,6 +6,7 @@ const TELEMETRY_EVENTS = {
6
6
  opened: "oss.inspector.opened",
7
7
  whatsNewViewed: "oss.inspector.whats_new_viewed",
8
8
  whatsNewSignalViewed: "oss.inspector.whats_new_signal_viewed",
9
+ errorSignalViewed: "oss.inspector.error_signal_viewed",
9
10
  whatsNewClicked: "oss.inspector.whats_new_clicked",
10
11
  threadsTabClicked: "oss.inspector.threads_tab_clicked",
11
12
  threadsLockedViewed: "oss.inspector.threads_locked_viewed",
@@ -25,7 +26,9 @@ const TELEMETRY_EVENTS = {
25
26
  homeViewed: "oss.inspector.home_viewed",
26
27
  homeCtaClicked: "oss.inspector.home_cta_clicked",
27
28
  metadataModuleViewed: "oss.inspector.metadata_module_viewed",
28
- metadataActionClicked: "oss.inspector.metadata_action_clicked"
29
+ metadataActionClicked: "oss.inspector.metadata_action_clicked",
30
+ eventSnippetsRun: "oss.inspector.event_snippets_run",
31
+ eventSnippetsSaved: "oss.inspector.event_snippets_saved"
29
32
  };
30
33
  const TELEMETRY_INGEST_URL = "https://telemetry.copilotkit.ai/ingest";
31
34
  const TELEMETRY_DOCS_URL = "https://docs.copilotkit.ai/telemetry";
@@ -88,6 +91,31 @@ function trackWhatsNewSignalViewed(props) {
88
91
  track(TELEMETRY_EVENTS.whatsNewSignalViewed, props);
89
92
  }
90
93
  /**
94
+ * Fires when the launcher's error signal becomes *visible* — not when it arms.
95
+ * Arming can happen with the panel open or the tab hidden, where there is no
96
+ * launcher to look at, so counting arms would inflate the denominator this
97
+ * event exists to provide.
98
+ *
99
+ * Deliberately carries three fixed enum values and nothing else. This is the
100
+ * one place a later change could casually attach a free-text field, and the
101
+ * failure message must never be transmitted.
102
+ *
103
+ * `label` is deliberately not a new event: the catalogue's size is asserted
104
+ * and spelled out in a test title, and a property answers the one open
105
+ * question — how often the no-room fallback fires — without touching either.
106
+ *
107
+ * The pill's own marginal effect cannot be measured here and must not be
108
+ * reverse-engineered from this data: it ships together with the dot and the
109
+ * beat, so there is no period with one and not the other.
110
+ */
111
+ function trackErrorSignalViewed(props) {
112
+ track(TELEMETRY_EVENTS.errorSignalViewed, {
113
+ source: props.source,
114
+ presentation: props.presentation,
115
+ label: props.label
116
+ });
117
+ }
118
+ /**
91
119
  * Fires for links followed inside the announcement body. `body` is the only
92
120
  * call-to-action left: dismissal no longer exists, so there is nothing else a
93
121
  * click on this surface can mean.
@@ -245,6 +273,20 @@ function trackMetadataActionClicked(props) {
245
273
  ...metadataCoarseProperties(props)
246
274
  });
247
275
  }
276
+ function trackEventSnippetsRun(props) {
277
+ track(TELEMETRY_EVENTS.eventSnippetsRun, {
278
+ recipe: props.recipe,
279
+ source: props.source,
280
+ success: props.success
281
+ });
282
+ }
283
+ function trackEventSnippetsSaved(props) {
284
+ track(TELEMETRY_EVENTS.eventSnippetsSaved, {
285
+ recipe: props.recipe,
286
+ source: props.source,
287
+ success: props.success
288
+ });
289
+ }
248
290
  /**
249
291
  * Returns the inspector's anonymous distinct-ID for cross-domain
250
292
  * propagation onto outbound announcement-CTA links, or `null` when the user
@@ -314,6 +356,9 @@ exports.ensureTelemetryDistinctId = ensureTelemetryDistinctId;
314
356
  exports.getRuntimeUrlType = getRuntimeUrlType;
315
357
  exports.getTelemetryDistinctIdForUrl = getTelemetryDistinctIdForUrl;
316
358
  exports.maybeShowDisclosure = maybeShowDisclosure;
359
+ exports.trackErrorSignalViewed = trackErrorSignalViewed;
360
+ exports.trackEventSnippetsRun = trackEventSnippetsRun;
361
+ exports.trackEventSnippetsSaved = trackEventSnippetsSaved;
317
362
  exports.trackHomeCtaClicked = trackHomeCtaClicked;
318
363
  exports.trackHomeViewed = trackHomeViewed;
319
364
  exports.trackInspectorOpened = trackInspectorOpened;