@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.
- package/dist/index.cjs +2418 -152
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +372 -6
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +372 -6
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2419 -156
- package/dist/index.mjs.map +1 -1
- package/dist/index.umd.js +3134 -236
- package/dist/index.umd.js.map +1 -1
- package/dist/lib/event-snippets.cjs +559 -0
- package/dist/lib/event-snippets.cjs.map +1 -0
- package/dist/lib/event-snippets.d.cts +61 -0
- package/dist/lib/event-snippets.d.cts.map +1 -0
- package/dist/lib/event-snippets.d.mts +61 -0
- package/dist/lib/event-snippets.d.mts.map +1 -0
- package/dist/lib/event-snippets.mjs +537 -0
- package/dist/lib/event-snippets.mjs.map +1 -0
- package/dist/lib/home-briefing.cjs +23 -3
- package/dist/lib/home-briefing.cjs.map +1 -1
- package/dist/lib/home-briefing.mjs +23 -4
- package/dist/lib/home-briefing.mjs.map +1 -1
- package/dist/lib/inspector-nav.cjs +1 -0
- package/dist/lib/inspector-nav.cjs.map +1 -1
- package/dist/lib/inspector-nav.d.cts +11 -0
- package/dist/lib/inspector-nav.d.cts.map +1 -0
- package/dist/lib/inspector-nav.d.mts +11 -0
- package/dist/lib/inspector-nav.d.mts.map +1 -0
- package/dist/lib/inspector-nav.mjs +1 -0
- package/dist/lib/inspector-nav.mjs.map +1 -1
- package/dist/lib/telemetry.cjs +46 -1
- package/dist/lib/telemetry.cjs.map +1 -1
- package/dist/lib/telemetry.d.cts.map +1 -1
- package/dist/lib/telemetry.d.mts.map +1 -1
- package/dist/lib/telemetry.mjs +44 -2
- package/dist/lib/telemetry.mjs.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.mjs +1 -1
- package/dist/styles/generated.cjs +1 -1
- package/dist/styles/generated.cjs.map +1 -1
- package/dist/styles/generated.mjs +1 -1
- package/dist/styles/generated.mjs.map +1 -1
- package/package.json +3 -3
|
@@ -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:
|
|
166
|
-
label:
|
|
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:
|
|
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":"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"}
|
|
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:
|
|
165
|
-
label:
|
|
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:
|
|
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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"}
|
|
@@ -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"}
|
|
@@ -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"}
|
package/dist/lib/telemetry.cjs
CHANGED
|
@@ -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;
|