@flanksource/clicky-ui 0.3.23 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/dist/data/ai/AdvancedChatConfig.cjs +3 -0
  2. package/dist/data/ai/AdvancedChatConfig.cjs.map +1 -1
  3. package/dist/data/ai/AdvancedChatConfig.d.ts +2 -1
  4. package/dist/data/ai/AdvancedChatConfig.d.ts.map +1 -1
  5. package/dist/data/ai/AdvancedChatConfig.js +3 -0
  6. package/dist/data/ai/AdvancedChatConfig.js.map +1 -1
  7. package/dist/data/ai/ChatWindow.cjs +77 -4
  8. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  9. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  10. package/dist/data/ai/ChatWindow.js +78 -5
  11. package/dist/data/ai/ChatWindow.js.map +1 -1
  12. package/dist/data/ai/ChatWindow.runtime.cjs +19 -4
  13. package/dist/data/ai/ChatWindow.runtime.cjs.map +1 -1
  14. package/dist/data/ai/ChatWindow.runtime.d.ts +2 -0
  15. package/dist/data/ai/ChatWindow.runtime.d.ts.map +1 -1
  16. package/dist/data/ai/ChatWindow.runtime.js +19 -4
  17. package/dist/data/ai/ChatWindow.runtime.js.map +1 -1
  18. package/dist/data/ai/ChatWindow.test-utils.d.ts +2 -1
  19. package/dist/data/ai/ChatWindow.test-utils.d.ts.map +1 -1
  20. package/dist/data/ai/ToolPreferences.cjs +8 -1
  21. package/dist/data/ai/ToolPreferences.cjs.map +1 -1
  22. package/dist/data/ai/ToolPreferences.d.ts +2 -1
  23. package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
  24. package/dist/data/ai/ToolPreferences.js +8 -1
  25. package/dist/data/ai/ToolPreferences.js.map +1 -1
  26. package/dist/data/chat/Chat.cjs +34 -7
  27. package/dist/data/chat/Chat.cjs.map +1 -1
  28. package/dist/data/chat/Chat.d.ts +9 -1
  29. package/dist/data/chat/Chat.d.ts.map +1 -1
  30. package/dist/data/chat/Chat.js +34 -7
  31. package/dist/data/chat/Chat.js.map +1 -1
  32. package/dist/data/chat/ChatRuntimeToolbar.cjs +13 -7
  33. package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
  34. package/dist/data/chat/ChatRuntimeToolbar.d.ts +2 -1
  35. package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
  36. package/dist/data/chat/ChatRuntimeToolbar.js +13 -7
  37. package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
  38. package/dist/data/chat/ContextMeter.cjs +64 -27
  39. package/dist/data/chat/ContextMeter.cjs.map +1 -1
  40. package/dist/data/chat/ContextMeter.d.ts +11 -4
  41. package/dist/data/chat/ContextMeter.d.ts.map +1 -1
  42. package/dist/data/chat/ContextMeter.js +64 -27
  43. package/dist/data/chat/ContextMeter.js.map +1 -1
  44. package/dist/data/chat/Message.cjs +36 -1
  45. package/dist/data/chat/Message.cjs.map +1 -1
  46. package/dist/data/chat/Message.d.ts +1 -1
  47. package/dist/data/chat/Message.d.ts.map +1 -1
  48. package/dist/data/chat/Message.js +37 -2
  49. package/dist/data/chat/Message.js.map +1 -1
  50. package/dist/data/chat/approval.cjs +17 -0
  51. package/dist/data/chat/approval.cjs.map +1 -1
  52. package/dist/data/chat/approval.d.ts +5 -0
  53. package/dist/data/chat/approval.d.ts.map +1 -1
  54. package/dist/data/chat/approval.js +17 -0
  55. package/dist/data/chat/approval.js.map +1 -1
  56. package/dist/data/chat/fork-seed.cjs +22 -0
  57. package/dist/data/chat/fork-seed.cjs.map +1 -0
  58. package/dist/data/chat/fork-seed.d.ts +8 -0
  59. package/dist/data/chat/fork-seed.d.ts.map +1 -0
  60. package/dist/data/chat/fork-seed.js +22 -0
  61. package/dist/data/chat/fork-seed.js.map +1 -0
  62. package/dist/data/chat/types.cjs.map +1 -1
  63. package/dist/data/chat/types.d.ts +19 -0
  64. package/dist/data/chat/types.d.ts.map +1 -1
  65. package/dist/data/chat/types.js.map +1 -1
  66. package/dist/data/chat/usage-snapshot.cjs +10 -1
  67. package/dist/data/chat/usage-snapshot.cjs.map +1 -1
  68. package/dist/data/chat/usage-snapshot.d.ts.map +1 -1
  69. package/dist/data/chat/usage-snapshot.js +10 -1
  70. package/dist/data/chat/usage-snapshot.js.map +1 -1
  71. package/dist/data/runtime/RuntimeBar.cjs +3 -0
  72. package/dist/data/runtime/RuntimeBar.cjs.map +1 -1
  73. package/dist/data/runtime/RuntimeBar.d.ts +3 -1
  74. package/dist/data/runtime/RuntimeBar.d.ts.map +1 -1
  75. package/dist/data/runtime/RuntimeBar.js +3 -0
  76. package/dist/data/runtime/RuntimeBar.js.map +1 -1
  77. package/dist/data/runtime/RuntimeBarCombo.cjs +15 -1
  78. package/dist/data/runtime/RuntimeBarCombo.cjs.map +1 -1
  79. package/dist/data/runtime/RuntimeBarCombo.d.ts +2 -1
  80. package/dist/data/runtime/RuntimeBarCombo.d.ts.map +1 -1
  81. package/dist/data/runtime/RuntimeBarCombo.js +15 -1
  82. package/dist/data/runtime/RuntimeBarCombo.js.map +1 -1
  83. package/dist/data/runtime/RuntimeBarSegments.cjs +7 -0
  84. package/dist/data/runtime/RuntimeBarSegments.cjs.map +1 -1
  85. package/dist/data/runtime/RuntimeBarSegments.d.ts +2 -1
  86. package/dist/data/runtime/RuntimeBarSegments.d.ts.map +1 -1
  87. package/dist/data/runtime/RuntimeBarSegments.js +7 -0
  88. package/dist/data/runtime/RuntimeBarSegments.js.map +1 -1
  89. package/dist/data/version-info.cjs +3 -3
  90. package/dist/data/version-info.js +3 -3
  91. package/dist/styles.css +1 -1
  92. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ToolPreferences.js","sources":["../../../src/data/ai/ToolPreferences.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Button } from \"../../components/button\";\nimport { UiCode2, UiCoins, UiShield, UiSliders } from \"../../icons\";\nimport { cn } from \"../../lib/utils\";\nimport { DropdownMenu } from \"../../overlay/DropdownMenu\";\nimport { Modal } from \"../../overlay/Modal\";\nimport { Icon } from \"../Icon\";\nimport { DEFAULT_REASONING_EFFORTS } from \"../chat/effort-icons\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n ToolAnnotations,\n ToolMeta,\n ToolMode,\n} from \"../chat/types\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport { AdvancedChatConfig } from \"./AdvancedChatConfig\";\nimport { AdvancedCostsPanel } from \"./AdvancedCostsPanel\";\nimport { ToolSchemaBrowser } from \"./ToolSchemaBrowser\";\nimport {\n CompactToolList,\n CompactToolPreferencesList,\n type CompactToolPreferencesListProps,\n} from \"./ToolPreferencesList\";\nimport { groupedToolEntries, type ToolGroup } from \"./ToolPreferences.model\";\n\nexport type { ClaudePermissionMode, ToolAnnotations, ToolMeta, ToolMode };\nexport { CompactToolPreferencesList, type CompactToolPreferencesListProps };\n\nexport type ToolPreferencesProps = {\n tools: ToolMeta[];\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n models?: ChatModel[] | undefined;\n runtime?: ChatModelRuntime | undefined;\n onRuntimeChange?: ((runtime: ChatModelRuntime) => void) | undefined;\n model?: string | undefined;\n onModelChange?: ((model: string) => void) | undefined;\n runtimeFamilies?: SpecRuntimeFamily[] | undefined;\n reasoningEfforts?: string[] | undefined;\n reasoningEffort?: string | undefined;\n onReasoningEffortChange?: ((effort: string) => void) | undefined;\n permissionMode?: ClaudePermissionMode | undefined;\n onPermissionModeChange?: ((mode: ClaudePermissionMode) => void) | undefined;\n temperature?: number | undefined;\n onTemperatureChange?: ((temperature: number | undefined) => void) | undefined;\n budget?: ChatBudgetConfig | undefined;\n onBudgetChange?: ((budget: ChatBudgetConfig) => void) | undefined;\n usage?: ChatUsageSummary | null | undefined;\n /** Base sessions endpoint for the Costs tab, e.g. `/api/chat/sessions`. */\n costsApi?: string | undefined;\n /** Thread whose costs the Costs tab reports. */\n threadId?: string | undefined;\n toolsLoading?: boolean | undefined;\n toolsError?: string | null | undefined;\n catalogLoading?: boolean | undefined;\n catalogError?: string | null | undefined;\n onCatalogRetry?: (() => void) | undefined;\n className?: string;\n};\n\ntype AdvancedTab = \"config\" | \"costs\" | \"permissions\" | \"browser\";\n\nconst ADVANCED_TABS: AdvancedTab[] = [\"config\", \"costs\", \"permissions\", \"browser\"];\n\nconst ADVANCED_TAB_ICONS: Record<AdvancedTab, typeof UiSliders> = {\n config: UiSliders,\n costs: UiCoins,\n permissions: UiShield,\n browser: UiCode2,\n};\n\nexport function ToolPreferences({\n tools,\n value,\n onChange,\n models = [],\n runtime,\n onRuntimeChange,\n model,\n onModelChange,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n reasoningEffort,\n onReasoningEffortChange,\n permissionMode = \"default\",\n onPermissionModeChange,\n temperature,\n onTemperatureChange,\n budget,\n onBudgetChange,\n usage,\n costsApi,\n threadId,\n toolsLoading = false,\n toolsError = null,\n catalogLoading = false,\n catalogError = null,\n onCatalogRetry,\n className = \"\",\n}: ToolPreferencesProps) {\n const [advancedOpen, setAdvancedOpen] = useState(false);\n const [advancedTab, setAdvancedTab] = useState<AdvancedTab>(\"config\");\n const groups = useMemo(() => groupedToolEntries(tools), [tools]);\n const resolvedRuntime =\n runtime ??\n ({\n ...(model ? { model } : {}),\n ...(reasoningEffort ? { effort: reasoningEffort } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n } satisfies ChatModelRuntime);\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n onRuntimeChange?.(next);\n if ((next.id ?? next.model ?? \"\") !== (model ?? \"\")) {\n onModelChange?.(next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (reasoningEffort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n return (\n <>\n <DropdownMenu\n align=\"right\"\n className={className}\n menuClassName=\"w-72 max-h-[70vh] overflow-y-auto p-1\"\n trigger={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title=\"Tool preferences\"\n data-testid=\"tool-preferences-btn\"\n >\n <Icon icon={UiSliders} className=\"size-4\" />\n </Button>\n }\n >\n {(closeMenu) => (\n <div>\n <div className=\"mb-1 px-1 text-xs font-semibold\">\n Tool Preferences\n </div>\n <CompactToolList\n groups={groups}\n value={value}\n onChange={onChange}\n emptyLabel=\"No tools available\"\n />\n <div className=\"mt-2 border-t border-border pt-2\">\n <button\n type=\"button\"\n className=\"w-full rounded px-2 py-1.5 text-left text-xs hover:bg-accent\"\n onClick={() => {\n closeMenu();\n setAdvancedOpen(true);\n }}\n >\n Advanced\n </button>\n </div>\n </div>\n )}\n </DropdownMenu>\n <Modal\n open={advancedOpen}\n onClose={() => setAdvancedOpen(false)}\n title=\"Advanced Chat Settings\"\n size=\"xl\"\n >\n <div className=\"flex h-[70vh] min-h-0 flex-col gap-3\">\n <div className=\"flex items-center gap-1 rounded border border-border bg-muted/30 p-1\">\n {ADVANCED_TABS.map((tab) => (\n <button\n key={tab}\n type=\"button\"\n className={cn(\n \"inline-flex h-8 items-center gap-1.5 rounded px-3 text-xs font-medium capitalize\",\n advancedTab === tab\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:bg-background/60\",\n )}\n onClick={() => setAdvancedTab(tab)}\n >\n <Icon icon={ADVANCED_TAB_ICONS[tab]} className=\"size-3.5\" />\n {tab}\n </button>\n ))}\n <div className=\"flex-1\" />\n {toolsLoading && (\n <span className=\"pr-2 text-[11px] text-muted-foreground\">\n Loading tools\n </span>\n )}\n {toolsError && (\n <span className=\"pr-2 text-[11px] text-destructive\">\n {toolsError}\n </span>\n )}\n </div>\n {advancedTab === \"config\" ? (\n <AdvancedChatConfig\n models={models}\n runtime={resolvedRuntime}\n onRuntimeChange={handleRuntimeChange}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n permissionMode={permissionMode}\n onPermissionModeChange={onPermissionModeChange}\n temperature={temperature}\n onTemperatureChange={onTemperatureChange}\n budget={budget}\n onBudgetChange={onBudgetChange}\n usage={usage}\n catalogLoading={catalogLoading}\n catalogError={catalogError}\n onCatalogRetry={onCatalogRetry}\n />\n ) : advancedTab === \"costs\" ? (\n <AdvancedCostsPanel costsApi={costsApi} threadId={threadId} />\n ) : advancedTab === \"permissions\" ? (\n <AdvancedPermissionsPanel\n groups={groups}\n value={value}\n onChange={onChange}\n />\n ) : (\n <ToolSchemaBrowser tools={tools} className=\"min-h-0 flex-1\" />\n )}\n </div>\n </Modal>\n </>\n );\n}\n\nfunction AdvancedPermissionsPanel({\n groups,\n value,\n onChange,\n}: {\n groups: ToolGroup[];\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n}) {\n return (\n <div className=\"min-h-0 flex-1 overflow-y-auto rounded border border-border p-1\">\n <CompactToolList\n groups={groups}\n value={value}\n onChange={onChange}\n emptyLabel=\"No tools\"\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ToolPreferences.js","sources":["../../../src/data/ai/ToolPreferences.tsx"],"sourcesContent":["import { useMemo, useState } from \"react\";\nimport { Button } from \"../../components/button\";\nimport { UiCode2, UiCoins, UiShield, UiSliders } from \"../../icons\";\nimport { cn } from \"../../lib/utils\";\nimport { DropdownMenu } from \"../../overlay/DropdownMenu\";\nimport { Modal } from \"../../overlay/Modal\";\nimport { Icon } from \"../Icon\";\nimport { DEFAULT_REASONING_EFFORTS } from \"../chat/effort-icons\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n ToolAnnotations,\n ToolMeta,\n ToolMode,\n} from \"../chat/types\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport { AdvancedChatConfig } from \"./AdvancedChatConfig\";\nimport { AdvancedCostsPanel } from \"./AdvancedCostsPanel\";\nimport { ToolSchemaBrowser } from \"./ToolSchemaBrowser\";\nimport {\n CompactToolList,\n CompactToolPreferencesList,\n type CompactToolPreferencesListProps,\n} from \"./ToolPreferencesList\";\nimport { groupedToolEntries, type ToolGroup } from \"./ToolPreferences.model\";\n\nexport type { ClaudePermissionMode, ToolAnnotations, ToolMeta, ToolMode };\nexport { CompactToolPreferencesList, type CompactToolPreferencesListProps };\n\nexport type ToolPreferencesProps = {\n tools: ToolMeta[];\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n models?: ChatModel[] | undefined;\n runtime?: ChatModelRuntime | undefined;\n onRuntimeChange?: ((runtime: ChatModelRuntime) => void) | undefined;\n model?: string | undefined;\n onModelChange?: ((model: string) => void) | undefined;\n runtimeFamilies?: SpecRuntimeFamily[] | undefined;\n reasoningEfforts?: string[] | undefined;\n runtimeLocked?: boolean | undefined;\n reasoningEffort?: string | undefined;\n onReasoningEffortChange?: ((effort: string) => void) | undefined;\n permissionMode?: ClaudePermissionMode | undefined;\n onPermissionModeChange?: ((mode: ClaudePermissionMode) => void) | undefined;\n temperature?: number | undefined;\n onTemperatureChange?: ((temperature: number | undefined) => void) | undefined;\n budget?: ChatBudgetConfig | undefined;\n onBudgetChange?: ((budget: ChatBudgetConfig) => void) | undefined;\n usage?: ChatUsageSummary | null | undefined;\n /** Base sessions endpoint for the Costs tab, e.g. `/api/chat/sessions`. */\n costsApi?: string | undefined;\n /** Thread whose costs the Costs tab reports. */\n threadId?: string | undefined;\n toolsLoading?: boolean | undefined;\n toolsError?: string | null | undefined;\n catalogLoading?: boolean | undefined;\n catalogError?: string | null | undefined;\n onCatalogRetry?: (() => void) | undefined;\n className?: string;\n};\n\ntype AdvancedTab = \"config\" | \"costs\" | \"permissions\" | \"browser\";\n\nconst ADVANCED_TABS: AdvancedTab[] = [\n \"config\",\n \"costs\",\n \"permissions\",\n \"browser\",\n];\n\nconst ADVANCED_TAB_ICONS: Record<AdvancedTab, typeof UiSliders> = {\n config: UiSliders,\n costs: UiCoins,\n permissions: UiShield,\n browser: UiCode2,\n};\n\nexport function ToolPreferences({\n tools,\n value,\n onChange,\n models = [],\n runtime,\n onRuntimeChange,\n model,\n onModelChange,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n runtimeLocked = false,\n reasoningEffort,\n onReasoningEffortChange,\n permissionMode = \"default\",\n onPermissionModeChange,\n temperature,\n onTemperatureChange,\n budget,\n onBudgetChange,\n usage,\n costsApi,\n threadId,\n toolsLoading = false,\n toolsError = null,\n catalogLoading = false,\n catalogError = null,\n onCatalogRetry,\n className = \"\",\n}: ToolPreferencesProps) {\n const [advancedOpen, setAdvancedOpen] = useState(false);\n const [advancedTab, setAdvancedTab] = useState<AdvancedTab>(\"config\");\n const groups = useMemo(() => groupedToolEntries(tools), [tools]);\n const resolvedRuntime =\n runtime ??\n ({\n ...(model ? { model } : {}),\n ...(reasoningEffort ? { effort: reasoningEffort } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\n } satisfies ChatModelRuntime);\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n onRuntimeChange?.(next);\n if ((next.id ?? next.model ?? \"\") !== (model ?? \"\")) {\n onModelChange?.(next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (reasoningEffort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n return (\n <>\n <DropdownMenu\n align=\"right\"\n className={className}\n menuClassName=\"w-72 max-h-[70vh] overflow-y-auto p-1\"\n trigger={\n <Button\n variant=\"ghost\"\n size=\"icon\"\n title=\"Tool preferences\"\n data-testid=\"tool-preferences-btn\"\n >\n <Icon icon={UiSliders} className=\"size-4\" />\n </Button>\n }\n >\n {(closeMenu) => (\n <div>\n <div className=\"mb-1 px-1 text-xs font-semibold\">\n Tool Preferences\n </div>\n <CompactToolList\n groups={groups}\n value={value}\n onChange={onChange}\n emptyLabel=\"No tools available\"\n />\n <div className=\"mt-2 border-t border-border pt-2\">\n <button\n type=\"button\"\n className=\"w-full rounded px-2 py-1.5 text-left text-xs hover:bg-accent\"\n onClick={() => {\n closeMenu();\n setAdvancedOpen(true);\n }}\n >\n Advanced\n </button>\n </div>\n </div>\n )}\n </DropdownMenu>\n <Modal\n open={advancedOpen}\n onClose={() => setAdvancedOpen(false)}\n title=\"Advanced Chat Settings\"\n size=\"xl\"\n >\n <div className=\"flex h-[70vh] min-h-0 flex-col gap-3\">\n <div className=\"flex items-center gap-1 rounded border border-border bg-muted/30 p-1\">\n {ADVANCED_TABS.map((tab) => (\n <button\n key={tab}\n type=\"button\"\n className={cn(\n \"inline-flex h-8 items-center gap-1.5 rounded px-3 text-xs font-medium capitalize\",\n advancedTab === tab\n ? \"bg-background text-foreground shadow-sm\"\n : \"text-muted-foreground hover:bg-background/60\",\n )}\n onClick={() => setAdvancedTab(tab)}\n >\n <Icon icon={ADVANCED_TAB_ICONS[tab]} className=\"size-3.5\" />\n {tab}\n </button>\n ))}\n <div className=\"flex-1\" />\n {toolsLoading && (\n <span className=\"pr-2 text-[11px] text-muted-foreground\">\n Loading tools\n </span>\n )}\n {toolsError && (\n <span className=\"pr-2 text-[11px] text-destructive\">\n {toolsError}\n </span>\n )}\n </div>\n {advancedTab === \"config\" ? (\n <AdvancedChatConfig\n models={models}\n runtime={resolvedRuntime}\n onRuntimeChange={handleRuntimeChange}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n runtimeLocked={runtimeLocked}\n permissionMode={permissionMode}\n onPermissionModeChange={onPermissionModeChange}\n temperature={temperature}\n onTemperatureChange={onTemperatureChange}\n budget={budget}\n onBudgetChange={onBudgetChange}\n usage={usage}\n catalogLoading={catalogLoading}\n catalogError={catalogError}\n onCatalogRetry={onCatalogRetry}\n />\n ) : advancedTab === \"costs\" ? (\n <AdvancedCostsPanel costsApi={costsApi} threadId={threadId} />\n ) : advancedTab === \"permissions\" ? (\n <AdvancedPermissionsPanel\n groups={groups}\n value={value}\n onChange={onChange}\n />\n ) : (\n <ToolSchemaBrowser tools={tools} className=\"min-h-0 flex-1\" />\n )}\n </div>\n </Modal>\n </>\n );\n}\n\nfunction AdvancedPermissionsPanel({\n groups,\n value,\n onChange,\n}: {\n groups: ToolGroup[];\n value: Record<string, ToolMode>;\n onChange: (prefs: Record<string, ToolMode>) => void;\n}) {\n return (\n <div className=\"min-h-0 flex-1 overflow-y-auto rounded border border-border p-1\">\n <CompactToolList\n groups={groups}\n value={value}\n onChange={onChange}\n emptyLabel=\"No tools\"\n />\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
@@ -11,6 +11,7 @@ const Suggestion = require("./Suggestion.cjs");
11
11
  const effortIcons = require("./effort-icons.cjs");
12
12
  const models = require("./models.cjs");
13
13
  const approval = require("./approval.cjs");
14
+ const forkSeed = require("./fork-seed.cjs");
14
15
  const Chat_runtime = require("./Chat.runtime.cjs");
15
16
  const attachmentUpload = require("./attachment-upload.cjs");
16
17
  const registry = require("./tool-render/registry.cjs");
@@ -43,6 +44,9 @@ function Chat({
43
44
  attachmentLimits,
44
45
  threadId,
45
46
  sessionsApi = null,
47
+ runtimeLocked = false,
48
+ onMessageCountChange,
49
+ onSessionHydrated,
46
50
  renderToolResult,
47
51
  tools,
48
52
  toolRenderers,
@@ -73,6 +77,12 @@ function Chat({
73
77
  );
74
78
  const lastDefaultModel = react.useRef(defaultModel);
75
79
  const sentInitialPromptId = react.useRef(null);
80
+ const activeThreadRef = react.useRef(threadId);
81
+ const onMessageCountChangeRef = react.useRef(onMessageCountChange);
82
+ const onSessionHydratedRef = react.useRef(onSessionHydrated);
83
+ activeThreadRef.current = threadId;
84
+ onMessageCountChangeRef.current = onMessageCountChange;
85
+ onSessionHydratedRef.current = onSessionHydrated;
76
86
  const runtime = controlledRuntime ?? internalRuntime;
77
87
  const toolRegistry = react.useMemo(
78
88
  () => registry.toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),
@@ -168,6 +178,7 @@ function Chat({
168
178
  setApprovalError(void 0);
169
179
  setMessagesRef.current([]);
170
180
  void approval.getChatSession(sessionsApi, threadId).then((session) => {
181
+ var _a;
171
182
  if (cancelled) return;
172
183
  if (session.id !== threadId) {
173
184
  throw new Error(
@@ -176,6 +187,7 @@ function Chat({
176
187
  }
177
188
  setMessagesRef.current(session.messages);
178
189
  setHydratedSessionId(session.id);
190
+ (_a = onSessionHydratedRef.current) == null ? void 0 : _a.call(onSessionHydratedRef, session);
179
191
  }).catch((cause) => {
180
192
  if (cancelled) return;
181
193
  setApprovalError(
@@ -186,6 +198,13 @@ function Chat({
186
198
  cancelled = true;
187
199
  };
188
200
  }, [sessionsApi, threadId]);
201
+ react.useEffect(() => {
202
+ var _a;
203
+ (_a = onMessageCountChangeRef.current) == null ? void 0 : _a.call(
204
+ onMessageCountChangeRef,
205
+ messages.filter((message) => !forkSeed.isForkSeedMessage(message)).length
206
+ );
207
+ }, [messages]);
189
208
  react.useEffect(() => {
190
209
  if (!initialPrompt || status !== "ready") return;
191
210
  if (sessionsApi && threadId && hydratedSessionId !== threadId) return;
@@ -211,15 +230,19 @@ function Chat({
211
230
  if (status !== "ready") return;
212
231
  const last = [...messages].reverse().find((m) => m.role === "assistant");
213
232
  const meta = last == null ? void 0 : last.metadata;
214
- if (!meta) return;
233
+ if (!meta) {
234
+ setUsage(null);
235
+ return;
236
+ }
237
+ const settledModel = meta.model ? Chat_runtime.selectedChatModel(models$1, { id: meta.model, model: meta.model }) : void 0;
215
238
  const snapshot = usageSnapshot.usageSnapshotFromMetadata(meta, {
216
- contextWindow: selectedModel == null ? void 0 : selectedModel.contextWindow,
217
- modelLabel: selectedModel == null ? void 0 : selectedModel.label,
239
+ contextWindow: settledModel == null ? void 0 : settledModel.contextWindow,
240
+ modelLabel: settledModel == null ? void 0 : settledModel.label,
218
241
  messageCount: messages.length
219
242
  });
220
243
  setUsage(snapshot);
221
244
  (_a = onUsageRef.current) == null ? void 0 : _a.call(onUsageRef, snapshot);
222
- }, [messages, status, selectedModel]);
245
+ }, [messages, models$1, status]);
223
246
  const handleRuntimeChange = (next) => {
224
247
  if (controlledRuntime === void 0) setInternalRuntime(next);
225
248
  onRuntimeChange == null ? void 0 : onRuntimeChange(next);
@@ -232,6 +255,7 @@ function Chat({
232
255
  }
233
256
  };
234
257
  const resolveToolApproval = async (id, approved, reason) => {
258
+ const approvalThreadId = threadId;
235
259
  setApprovalError(void 0);
236
260
  try {
237
261
  if (!sessionsApi) {
@@ -241,18 +265,20 @@ function Chat({
241
265
  }
242
266
  const session = await approval.postToolApproval({
243
267
  sessionsApi,
244
- sessionId: threadId ?? "",
268
+ sessionId: approvalThreadId ?? "",
245
269
  approvalId: id,
246
270
  approved,
247
271
  ...reason ? { reason } : {}
248
272
  });
249
- if (session.id !== threadId) {
273
+ if (activeThreadRef.current !== approvalThreadId) return;
274
+ if (session.id !== approvalThreadId) {
250
275
  throw new Error(
251
- `Captain chat session response ID "${session.id}" does not match active session "${threadId ?? ""}".`
276
+ `Captain chat session response ID "${session.id}" does not match active session "${approvalThreadId ?? ""}".`
252
277
  );
253
278
  }
254
279
  setMessages(session.messages);
255
280
  } catch (cause) {
281
+ if (activeThreadRef.current !== approvalThreadId) return;
256
282
  setApprovalError(
257
283
  cause instanceof Error ? cause : new Error(String(cause))
258
284
  );
@@ -268,6 +294,7 @@ function Chat({
268
294
  selectedModel,
269
295
  usage,
270
296
  threadId,
297
+ locked: runtimeLocked,
271
298
  onRuntimeChange: handleRuntimeChange
272
299
  }
273
300
  );
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.cjs","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type ChatTransport, type UIMessage } from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport { getChatSession, postToolApproval } from \"./approval\";\nimport {\n hasStructuredRuntime,\n resolveChatRuntime,\n selectedChatModel,\n} from \"./Chat.runtime\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport { toToolRenderRegistry } from \"./tool-render/registry\";\nimport { ToolRenderRegistryProvider } from \"./tool-render/context\";\nimport { ChatRuntimeToolbar } from \"./ChatRuntimeToolbar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport type {\n ToolRenderAdapter,\n ToolRenderRegistry,\n} from \"./tool-render/adapter\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolMeta,\n ToolResultRenderer,\n} from \"./types\";\nimport { usageSnapshotFromMetadata } from \"./usage-snapshot\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Controlled selected model id. */\n model?: string;\n /** Initially selected structured runtime. */\n defaultRuntime?: ChatModelRuntime;\n /** Controlled structured runtime. */\n runtime?: ChatModelRuntime;\n /** Runtime families and their host availability. */\n runtimeFamilies?: SpecRuntimeFamily[];\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Controlled selected reasoning effort (\"\" = none). */\n reasoningEffort?: string;\n /** Sampling temperature forwarded to the backend when set. */\n temperature?: number;\n /** Per-request budget forwarded to the backend when set. */\n budget?: ChatBudgetConfig;\n /** Claude Agent SDK permission mode forwarded to the backend when set. */\n permissionMode?: ClaudePermissionMode;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified when the user changes any runtime field. */\n onRuntimeChange?: (runtime: ChatModelRuntime) => void;\n /** Notified when the user changes reasoning effort. */\n onReasoningEffortChange?: (effort: string) => void;\n /** Notified when the user changes Claude permission mode. */\n onPermissionModeChange?: (mode: ClaudePermissionMode) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** Upload endpoint used before an attachment is added to a chat message. */\n attachmentsApi?: string;\n /** Custom durable upload adapter; overrides attachmentsApi. */\n attachmentUpload?: AttachmentUploadAdapter;\n attachmentLimits?: AttachmentLimits;\n /** Captain thread id used as the AI SDK chat id and forwarded in the body. */\n threadId?: string;\n /** Canonical Captain session endpoint used to hydrate messages and resolve\n * approvals. The session and approval ids are appended to this URL. */\n sessionsApi?: string | null;\n /** Optional host renderer for recognized completed tool outputs. Takes\n * priority over `toolRenderers` on the output surface. */\n renderToolResult?: ToolResultRenderer;\n /** Tool catalog, used to resolve each call's input/output JSON Schema so\n * params and results render with real labels instead of raw JSON. */\n tools?: ToolMeta[];\n /** Domain tool renderers. Host adapters are matched before the built-in\n * heuristics, which stay as the floor. */\n toolRenderers?: ToolRenderAdapter[] | ToolRenderRegistry;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n /** Prompt to send automatically once. A new `id` sends even when text repeats. */\n initialPrompt?: { id: number; text: string } | null;\n /** Called after `initialPrompt` has been handed to the chat transport. */\n onInitialPromptSent?: () => void;\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, permission mode and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n model: controlledModel,\n defaultRuntime,\n runtime: controlledRuntime,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort,\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onRuntimeChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n sessionsApi = null,\n renderToolResult,\n tools,\n toolRenderers,\n body,\n transport,\n initialMessages,\n initialPrompt,\n onInitialPromptSent,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [internalRuntime, setInternalRuntime] = useState<ChatModelRuntime>(\n () =>\n controlledRuntime ??\n resolveChatRuntime({\n models: modelsProp ?? [],\n current: defaultRuntime,\n preferredModel:\n controlledModel ??\n (defaultRuntime\n ? undefined\n : (defaultModel ?? defaultChatModelId(modelsProp ?? []))),\n effort: controlledEffort ?? defaultReasoningEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const [approvalError, setApprovalError] = useState<Error | undefined>();\n const [hydratedSessionId, setHydratedSessionId] = useState<string | null>(\n null,\n );\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n const runtime = controlledRuntime ?? internalRuntime;\n\n // Provided once here; <ToolCall> reads it from context, so no tool-render\n // props thread through Conversation/Message.\n const toolRegistry = useMemo(\n () => toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),\n [toolRenderers, tools],\n );\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n }, [modelsProp]);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) =>\n r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`)),\n )\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n })\n .catch((err) =>\n console.warn(\"clicky-ui: failed to load chat models\", err),\n );\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (controlledRuntime !== undefined) return;\n const defaultChanged =\n Boolean(defaultModel) && defaultModel !== lastDefaultModel.current;\n lastDefaultModel.current = defaultModel;\n setInternalRuntime((current) =>\n resolveChatRuntime({\n models,\n current,\n preferredModel:\n controlledModel ??\n (defaultChanged\n ? defaultModel\n : (selectedChatModel(models, current)?.id ??\n defaultModel ??\n defaultChatModelId(models))),\n effort: controlledEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n }, [\n controlledEffort,\n controlledModel,\n controlledRuntime,\n defaultModel,\n models,\n reasoningEfforts,\n temperature,\n ]);\n\n const selectedModel = selectedChatModel(models, runtime);\n const structuredRuntime = hasStructuredRuntime(runtime, selectedModel);\n const resolvedAttachmentUpload = useMemo(\n () =>\n attachmentUpload ??\n createAttachmentUploadAdapter({ endpoint: attachmentsApi }),\n [attachmentUpload, attachmentsApi],\n );\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/runtime selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(structuredRuntime\n ? { runtime }\n : {\n ...(runtime.model ? { model: runtime.model } : {}),\n ...(runtime.effort ? { reasoningEffort: runtime.effort } : {}),\n ...(runtime.temperature !== undefined\n ? { temperature: runtime.temperature }\n : {}),\n }),\n ...(budget && (budget.cost !== undefined || budget.maxTokens !== undefined)\n ? { budget }\n : {}),\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () =>\n transport ??\n new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const {\n messages,\n setMessages,\n sendMessage,\n regenerate,\n status,\n error,\n clearError,\n stop,\n } = useChat<ChatUIMessage>({\n transport: resolvedTransport,\n ...(threadId ? { id: threadId } : {}),\n ...(initialMessages\n ? { messages: initialMessages as ChatUIMessage[] }\n : {}),\n });\n const setMessagesRef = useRef(setMessages);\n setMessagesRef.current = setMessages;\n\n useEffect(() => {\n if (!sessionsApi || !threadId) return;\n let cancelled = false;\n setHydratedSessionId(null);\n setApprovalError(undefined);\n setMessagesRef.current([]);\n void getChatSession(sessionsApi, threadId)\n .then((session) => {\n if (cancelled) return;\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match requested session \"${threadId}\".`,\n );\n }\n setMessagesRef.current(session.messages as ChatUIMessage[]);\n setHydratedSessionId(session.id);\n })\n .catch((cause) => {\n if (cancelled) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [sessionsApi, threadId]);\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") return;\n if (sessionsApi && threadId && hydratedSessionId !== threadId) return;\n if (sentInitialPromptId.current === initialPrompt.id) return;\n const text = initialPrompt.text.trim();\n if (!text) return;\n sentInitialPromptId.current = initialPrompt.id;\n void sendMessage({ text });\n onInitialPromptSent?.();\n }, [\n hydratedSessionId,\n initialPrompt,\n onInitialPromptSent,\n sendMessage,\n sessionsApi,\n status,\n threadId,\n ]);\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const snapshot = usageSnapshotFromMetadata(meta, {\n contextWindow: selectedModel?.contextWindow,\n modelLabel: selectedModel?.label,\n messageCount: messages.length,\n });\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n if (controlledRuntime === undefined) setInternalRuntime(next);\n onRuntimeChange?.(next);\n const nextModel = selectedChatModel(models, next);\n if (nextModel?.id !== selectedModel?.id) {\n onModelChange?.(nextModel?.id ?? next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (runtime.effort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n const resolveToolApproval = async (\n id: string,\n approved: boolean,\n reason?: string,\n ) => {\n setApprovalError(undefined);\n try {\n if (!sessionsApi) {\n throw new Error(\n \"A Captain sessions API is required to resolve tool approvals.\",\n );\n }\n const session = await postToolApproval({\n sessionsApi,\n sessionId: threadId ?? \"\",\n approvalId: id,\n approved,\n ...(reason ? { reason } : {}),\n });\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match active session \"${threadId ?? \"\"}\".`,\n );\n }\n setMessages(session.messages as ChatUIMessage[]);\n } catch (cause) {\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n };\n\n const toolbar = (\n <ChatRuntimeToolbar\n models={models}\n runtime={runtime}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n selectedModel={selectedModel}\n usage={usage}\n threadId={threadId}\n onRuntimeChange={handleRuntimeChange}\n />\n );\n\n const empty =\n messages.length === 0 && (emptyState || suggestions?.length) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions\n suggestions={suggestions}\n onSelect={(text) => void sendMessage({ text })}\n />\n )}\n </div>\n ) : undefined;\n\n return (\n <ToolRenderRegistryProvider value={toolRegistry}>\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={approvalError ?? error}\n onClearError={() => {\n setApprovalError(undefined);\n clearError();\n }}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(selectedModel?.id\n ? { model: selectedModel.id }\n : runtime.model\n ? { model: runtime.model }\n : {})}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void resolveToolApproval(id, approved, reason)\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n attachmentUpload={resolvedAttachmentUpload}\n {...(selectedModel?.inputMediaTypes\n ? { acceptedMediaTypes: selectedModel.inputMediaTypes }\n : {})}\n {...(attachmentLimits ? { attachmentLimits } : {})}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n </ToolRenderRegistryProvider>\n );\n}\n"],"names":["DEFAULT_REASONING_EFFORTS","attachmentUpload","models","useState","resolveChatRuntime","defaultChatModelId","useRef","useMemo","toToolRenderRegistry","useEffect","selectedChatModel","hasStructuredRuntime","createAttachmentUploadAdapter","DefaultChatTransport","useChat","getChatSession","usageSnapshotFromMetadata","postToolApproval","jsx","ChatRuntimeToolbar","jsxs","Suggestions","ToolRenderRegistryProvider","cn","Conversation","PromptInput"],"mappings":"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"}
1
+ {"version":3,"file":"Chat.cjs","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type ChatTransport, type UIMessage } from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport {\n getChatSession,\n postToolApproval,\n type CaptainChatSession,\n} from \"./approval\";\nimport { isForkSeedMessage } from \"./fork-seed\";\nimport {\n hasStructuredRuntime,\n resolveChatRuntime,\n selectedChatModel,\n} from \"./Chat.runtime\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport { toToolRenderRegistry } from \"./tool-render/registry\";\nimport { ToolRenderRegistryProvider } from \"./tool-render/context\";\nimport { ChatRuntimeToolbar } from \"./ChatRuntimeToolbar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport type {\n ToolRenderAdapter,\n ToolRenderRegistry,\n} from \"./tool-render/adapter\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolMeta,\n ToolResultRenderer,\n} from \"./types\";\nimport { usageSnapshotFromMetadata } from \"./usage-snapshot\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Controlled selected model id. */\n model?: string;\n /** Initially selected structured runtime. */\n defaultRuntime?: ChatModelRuntime;\n /** Controlled structured runtime. */\n runtime?: ChatModelRuntime;\n /** Runtime families and their host availability. */\n runtimeFamilies?: SpecRuntimeFamily[];\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Controlled selected reasoning effort (\"\" = none). */\n reasoningEffort?: string;\n /** Sampling temperature forwarded to the backend when set. */\n temperature?: number;\n /** Per-request budget forwarded to the backend when set. */\n budget?: ChatBudgetConfig;\n /** Claude Agent SDK permission mode forwarded to the backend when set. */\n permissionMode?: ClaudePermissionMode;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified when the user changes any runtime field. */\n onRuntimeChange?: (runtime: ChatModelRuntime) => void;\n /** Notified when the user changes reasoning effort. */\n onReasoningEffortChange?: (effort: string) => void;\n /** Notified when the user changes Claude permission mode. */\n onPermissionModeChange?: (mode: ClaudePermissionMode) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** Upload endpoint used before an attachment is added to a chat message. */\n attachmentsApi?: string;\n /** Custom durable upload adapter; overrides attachmentsApi. */\n attachmentUpload?: AttachmentUploadAdapter;\n attachmentLimits?: AttachmentLimits;\n /** Captain thread id used as the AI SDK chat id and forwarded in the body. */\n threadId?: string;\n /** Canonical Captain session endpoint used to hydrate messages and resolve\n * approvals. The session and approval ids are appended to this URL. */\n sessionsApi?: string | null;\n /** Prevents changing model/backend for an existing Captain session. Effort\n * and other per-turn generation settings remain editable. */\n runtimeLocked?: boolean;\n /** Reports the number of real (non-fork-seed) persisted/UI messages. */\n onMessageCountChange?: (count: number) => void;\n /** Reports the authoritative Captain session after detail hydration. */\n onSessionHydrated?: (session: CaptainChatSession) => void;\n /** Optional host renderer for recognized completed tool outputs. Takes\n * priority over `toolRenderers` on the output surface. */\n renderToolResult?: ToolResultRenderer;\n /** Tool catalog, used to resolve each call's input/output JSON Schema so\n * params and results render with real labels instead of raw JSON. */\n tools?: ToolMeta[];\n /** Domain tool renderers. Host adapters are matched before the built-in\n * heuristics, which stay as the floor. */\n toolRenderers?: ToolRenderAdapter[] | ToolRenderRegistry;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n /** Prompt to send automatically once. A new `id` sends even when text repeats. */\n initialPrompt?: { id: number; text: string } | null;\n /** Called after `initialPrompt` has been handed to the chat transport. */\n onInitialPromptSent?: () => void;\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, permission mode and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n model: controlledModel,\n defaultRuntime,\n runtime: controlledRuntime,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort,\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onRuntimeChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n sessionsApi = null,\n runtimeLocked = false,\n onMessageCountChange,\n onSessionHydrated,\n renderToolResult,\n tools,\n toolRenderers,\n body,\n transport,\n initialMessages,\n initialPrompt,\n onInitialPromptSent,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [internalRuntime, setInternalRuntime] = useState<ChatModelRuntime>(\n () =>\n controlledRuntime ??\n resolveChatRuntime({\n models: modelsProp ?? [],\n current: defaultRuntime,\n preferredModel:\n controlledModel ??\n (defaultRuntime\n ? undefined\n : (defaultModel ?? defaultChatModelId(modelsProp ?? []))),\n effort: controlledEffort ?? defaultReasoningEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const [approvalError, setApprovalError] = useState<Error | undefined>();\n const [hydratedSessionId, setHydratedSessionId] = useState<string | null>(\n null,\n );\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n const activeThreadRef = useRef(threadId);\n const onMessageCountChangeRef = useRef(onMessageCountChange);\n const onSessionHydratedRef = useRef(onSessionHydrated);\n activeThreadRef.current = threadId;\n onMessageCountChangeRef.current = onMessageCountChange;\n onSessionHydratedRef.current = onSessionHydrated;\n const runtime = controlledRuntime ?? internalRuntime;\n\n // Provided once here; <ToolCall> reads it from context, so no tool-render\n // props thread through Conversation/Message.\n const toolRegistry = useMemo(\n () => toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),\n [toolRenderers, tools],\n );\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n }, [modelsProp]);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) =>\n r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`)),\n )\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n })\n .catch((err) =>\n console.warn(\"clicky-ui: failed to load chat models\", err),\n );\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (controlledRuntime !== undefined) return;\n const defaultChanged =\n Boolean(defaultModel) && defaultModel !== lastDefaultModel.current;\n lastDefaultModel.current = defaultModel;\n setInternalRuntime((current) =>\n resolveChatRuntime({\n models,\n current,\n preferredModel:\n controlledModel ??\n (defaultChanged\n ? defaultModel\n : (selectedChatModel(models, current)?.id ??\n defaultModel ??\n defaultChatModelId(models))),\n effort: controlledEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n }, [\n controlledEffort,\n controlledModel,\n controlledRuntime,\n defaultModel,\n models,\n reasoningEfforts,\n temperature,\n ]);\n\n const selectedModel = selectedChatModel(models, runtime);\n const structuredRuntime = hasStructuredRuntime(runtime, selectedModel);\n const resolvedAttachmentUpload = useMemo(\n () =>\n attachmentUpload ??\n createAttachmentUploadAdapter({ endpoint: attachmentsApi }),\n [attachmentUpload, attachmentsApi],\n );\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/runtime selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(structuredRuntime\n ? { runtime }\n : {\n ...(runtime.model ? { model: runtime.model } : {}),\n ...(runtime.effort ? { reasoningEffort: runtime.effort } : {}),\n ...(runtime.temperature !== undefined\n ? { temperature: runtime.temperature }\n : {}),\n }),\n ...(budget && (budget.cost !== undefined || budget.maxTokens !== undefined)\n ? { budget }\n : {}),\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () =>\n transport ??\n new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const {\n messages,\n setMessages,\n sendMessage,\n regenerate,\n status,\n error,\n clearError,\n stop,\n } = useChat<ChatUIMessage>({\n transport: resolvedTransport,\n ...(threadId ? { id: threadId } : {}),\n ...(initialMessages\n ? { messages: initialMessages as ChatUIMessage[] }\n : {}),\n });\n const setMessagesRef = useRef(setMessages);\n setMessagesRef.current = setMessages;\n\n useEffect(() => {\n if (!sessionsApi || !threadId) return;\n let cancelled = false;\n setHydratedSessionId(null);\n setApprovalError(undefined);\n setMessagesRef.current([]);\n void getChatSession(sessionsApi, threadId)\n .then((session) => {\n if (cancelled) return;\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match requested session \"${threadId}\".`,\n );\n }\n setMessagesRef.current(session.messages as ChatUIMessage[]);\n setHydratedSessionId(session.id);\n onSessionHydratedRef.current?.(session);\n })\n .catch((cause) => {\n if (cancelled) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [sessionsApi, threadId]);\n\n useEffect(() => {\n onMessageCountChangeRef.current?.(\n messages.filter((message) => !isForkSeedMessage(message)).length,\n );\n }, [messages]);\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") return;\n if (sessionsApi && threadId && hydratedSessionId !== threadId) return;\n if (sentInitialPromptId.current === initialPrompt.id) return;\n const text = initialPrompt.text.trim();\n if (!text) return;\n sentInitialPromptId.current = initialPrompt.id;\n void sendMessage({ text });\n onInitialPromptSent?.();\n }, [\n hydratedSessionId,\n initialPrompt,\n onInitialPromptSent,\n sendMessage,\n sessionsApi,\n status,\n threadId,\n ]);\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) {\n setUsage(null);\n return;\n }\n const settledModel = meta.model\n ? selectedChatModel(models, { id: meta.model, model: meta.model })\n : undefined;\n const snapshot = usageSnapshotFromMetadata(meta, {\n contextWindow: settledModel?.contextWindow,\n modelLabel: settledModel?.label,\n messageCount: messages.length,\n });\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, models, status]);\n\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n if (controlledRuntime === undefined) setInternalRuntime(next);\n onRuntimeChange?.(next);\n const nextModel = selectedChatModel(models, next);\n if (nextModel?.id !== selectedModel?.id) {\n onModelChange?.(nextModel?.id ?? next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (runtime.effort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n const resolveToolApproval = async (\n id: string,\n approved: boolean,\n reason?: string,\n ) => {\n const approvalThreadId = threadId;\n setApprovalError(undefined);\n try {\n if (!sessionsApi) {\n throw new Error(\n \"A Captain sessions API is required to resolve tool approvals.\",\n );\n }\n const session = await postToolApproval({\n sessionsApi,\n sessionId: approvalThreadId ?? \"\",\n approvalId: id,\n approved,\n ...(reason ? { reason } : {}),\n });\n if (activeThreadRef.current !== approvalThreadId) return;\n if (session.id !== approvalThreadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match active session \"${approvalThreadId ?? \"\"}\".`,\n );\n }\n setMessages(session.messages as ChatUIMessage[]);\n } catch (cause) {\n if (activeThreadRef.current !== approvalThreadId) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n };\n\n const toolbar = (\n <ChatRuntimeToolbar\n models={models}\n runtime={runtime}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n selectedModel={selectedModel}\n usage={usage}\n threadId={threadId}\n locked={runtimeLocked}\n onRuntimeChange={handleRuntimeChange}\n />\n );\n\n const empty =\n messages.length === 0 && (emptyState || suggestions?.length) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions\n suggestions={suggestions}\n onSelect={(text) => void sendMessage({ text })}\n />\n )}\n </div>\n ) : undefined;\n\n return (\n <ToolRenderRegistryProvider value={toolRegistry}>\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={approvalError ?? error}\n onClearError={() => {\n setApprovalError(undefined);\n clearError();\n }}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(selectedModel?.id\n ? { model: selectedModel.id }\n : runtime.model\n ? { model: runtime.model }\n : {})}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void resolveToolApproval(id, approved, reason)\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n attachmentUpload={resolvedAttachmentUpload}\n {...(selectedModel?.inputMediaTypes\n ? { acceptedMediaTypes: selectedModel.inputMediaTypes }\n : {})}\n {...(attachmentLimits ? { attachmentLimits } : {})}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n </ToolRenderRegistryProvider>\n );\n}\n"],"names":["DEFAULT_REASONING_EFFORTS","attachmentUpload","models","useState","resolveChatRuntime","defaultChatModelId","useRef","useMemo","toToolRenderRegistry","useEffect","selectedChatModel","hasStructuredRuntime","createAttachmentUploadAdapter","DefaultChatTransport","useChat","getChatSession","isForkSeedMessage","usageSnapshotFromMetadata","postToolApproval","jsx","ChatRuntimeToolbar","jsxs","Suggestions","ToolRenderRegistryProvider","cn","Conversation","PromptInput"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { ChatTransport, UIMessage } from 'ai';
2
+ import { CaptainChatSession } from './approval';
2
3
  import { AttachmentLimits, AttachmentUploadAdapter } from './attachment-upload';
3
4
  import { SpecRuntimeFamily } from '../runtime/runtime-mode';
4
5
  import { ToolRenderAdapter, ToolRenderRegistry } from './tool-render/adapter';
@@ -59,6 +60,13 @@ export type ChatProps = {
59
60
  /** Canonical Captain session endpoint used to hydrate messages and resolve
60
61
  * approvals. The session and approval ids are appended to this URL. */
61
62
  sessionsApi?: string | null;
63
+ /** Prevents changing model/backend for an existing Captain session. Effort
64
+ * and other per-turn generation settings remain editable. */
65
+ runtimeLocked?: boolean;
66
+ /** Reports the number of real (non-fork-seed) persisted/UI messages. */
67
+ onMessageCountChange?: (count: number) => void;
68
+ /** Reports the authoritative Captain session after detail hydration. */
69
+ onSessionHydrated?: (session: CaptainChatSession) => void;
62
70
  /** Optional host renderer for recognized completed tool outputs. Takes
63
71
  * priority over `toolRenderers` on the output surface. */
64
72
  renderToolResult?: ToolResultRenderer;
@@ -90,5 +98,5 @@ export type ChatProps = {
90
98
  * per-message copy/regenerate, and human-in-the-loop tool approvals. The
91
99
  * backend owns model selection and tool execution; the selected model,
92
100
  * effort, permission mode and thread id are forwarded in the request body. */
93
- export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, model: controlledModel, defaultRuntime, runtime: controlledRuntime, runtimeFamilies, reasoningEfforts, defaultReasoningEffort, reasoningEffort: controlledEffort, temperature, budget, permissionMode, onModelChange, onRuntimeChange, onReasoningEffortChange, onUsage, suggestions, enableAttachments, attachmentsApi, attachmentUpload, attachmentLimits, threadId, sessionsApi, renderToolResult, tools, toolRenderers, body, transport, initialMessages, initialPrompt, onInitialPromptSent, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
101
+ export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, model: controlledModel, defaultRuntime, runtime: controlledRuntime, runtimeFamilies, reasoningEfforts, defaultReasoningEffort, reasoningEffort: controlledEffort, temperature, budget, permissionMode, onModelChange, onRuntimeChange, onReasoningEffortChange, onUsage, suggestions, enableAttachments, attachmentsApi, attachmentUpload, attachmentLimits, threadId, sessionsApi, runtimeLocked, onMessageCountChange, onSessionHydrated, renderToolResult, tools, toolRenderers, body, transport, initialMessages, initialPrompt, onInitialPromptSent, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
94
102
  //# sourceMappingURL=Chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Chat.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Chat.tsx"],"names":[],"mappings":"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"}
@@ -9,6 +9,7 @@ import { Suggestions } from "./Suggestion.js";
9
9
  import { DEFAULT_REASONING_EFFORTS } from "./effort-icons.js";
10
10
  import { defaultChatModelId } from "./models.js";
11
11
  import { getChatSession, postToolApproval } from "./approval.js";
12
+ import { isForkSeedMessage } from "./fork-seed.js";
12
13
  import { resolveChatRuntime, selectedChatModel, hasStructuredRuntime } from "./Chat.runtime.js";
13
14
  import { createAttachmentUploadAdapter } from "./attachment-upload.js";
14
15
  import { toToolRenderRegistry } from "./tool-render/registry.js";
@@ -41,6 +42,9 @@ function Chat({
41
42
  attachmentLimits,
42
43
  threadId,
43
44
  sessionsApi = null,
45
+ runtimeLocked = false,
46
+ onMessageCountChange,
47
+ onSessionHydrated,
44
48
  renderToolResult,
45
49
  tools,
46
50
  toolRenderers,
@@ -71,6 +75,12 @@ function Chat({
71
75
  );
72
76
  const lastDefaultModel = useRef(defaultModel);
73
77
  const sentInitialPromptId = useRef(null);
78
+ const activeThreadRef = useRef(threadId);
79
+ const onMessageCountChangeRef = useRef(onMessageCountChange);
80
+ const onSessionHydratedRef = useRef(onSessionHydrated);
81
+ activeThreadRef.current = threadId;
82
+ onMessageCountChangeRef.current = onMessageCountChange;
83
+ onSessionHydratedRef.current = onSessionHydrated;
74
84
  const runtime = controlledRuntime ?? internalRuntime;
75
85
  const toolRegistry = useMemo(
76
86
  () => toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),
@@ -166,6 +176,7 @@ function Chat({
166
176
  setApprovalError(void 0);
167
177
  setMessagesRef.current([]);
168
178
  void getChatSession(sessionsApi, threadId).then((session) => {
179
+ var _a;
169
180
  if (cancelled) return;
170
181
  if (session.id !== threadId) {
171
182
  throw new Error(
@@ -174,6 +185,7 @@ function Chat({
174
185
  }
175
186
  setMessagesRef.current(session.messages);
176
187
  setHydratedSessionId(session.id);
188
+ (_a = onSessionHydratedRef.current) == null ? void 0 : _a.call(onSessionHydratedRef, session);
177
189
  }).catch((cause) => {
178
190
  if (cancelled) return;
179
191
  setApprovalError(
@@ -184,6 +196,13 @@ function Chat({
184
196
  cancelled = true;
185
197
  };
186
198
  }, [sessionsApi, threadId]);
199
+ useEffect(() => {
200
+ var _a;
201
+ (_a = onMessageCountChangeRef.current) == null ? void 0 : _a.call(
202
+ onMessageCountChangeRef,
203
+ messages.filter((message) => !isForkSeedMessage(message)).length
204
+ );
205
+ }, [messages]);
187
206
  useEffect(() => {
188
207
  if (!initialPrompt || status !== "ready") return;
189
208
  if (sessionsApi && threadId && hydratedSessionId !== threadId) return;
@@ -209,15 +228,19 @@ function Chat({
209
228
  if (status !== "ready") return;
210
229
  const last = [...messages].reverse().find((m) => m.role === "assistant");
211
230
  const meta = last == null ? void 0 : last.metadata;
212
- if (!meta) return;
231
+ if (!meta) {
232
+ setUsage(null);
233
+ return;
234
+ }
235
+ const settledModel = meta.model ? selectedChatModel(models, { id: meta.model, model: meta.model }) : void 0;
213
236
  const snapshot = usageSnapshotFromMetadata(meta, {
214
- contextWindow: selectedModel == null ? void 0 : selectedModel.contextWindow,
215
- modelLabel: selectedModel == null ? void 0 : selectedModel.label,
237
+ contextWindow: settledModel == null ? void 0 : settledModel.contextWindow,
238
+ modelLabel: settledModel == null ? void 0 : settledModel.label,
216
239
  messageCount: messages.length
217
240
  });
218
241
  setUsage(snapshot);
219
242
  (_a = onUsageRef.current) == null ? void 0 : _a.call(onUsageRef, snapshot);
220
- }, [messages, status, selectedModel]);
243
+ }, [messages, models, status]);
221
244
  const handleRuntimeChange = (next) => {
222
245
  if (controlledRuntime === void 0) setInternalRuntime(next);
223
246
  onRuntimeChange == null ? void 0 : onRuntimeChange(next);
@@ -230,6 +253,7 @@ function Chat({
230
253
  }
231
254
  };
232
255
  const resolveToolApproval = async (id, approved, reason) => {
256
+ const approvalThreadId = threadId;
233
257
  setApprovalError(void 0);
234
258
  try {
235
259
  if (!sessionsApi) {
@@ -239,18 +263,20 @@ function Chat({
239
263
  }
240
264
  const session = await postToolApproval({
241
265
  sessionsApi,
242
- sessionId: threadId ?? "",
266
+ sessionId: approvalThreadId ?? "",
243
267
  approvalId: id,
244
268
  approved,
245
269
  ...reason ? { reason } : {}
246
270
  });
247
- if (session.id !== threadId) {
271
+ if (activeThreadRef.current !== approvalThreadId) return;
272
+ if (session.id !== approvalThreadId) {
248
273
  throw new Error(
249
- `Captain chat session response ID "${session.id}" does not match active session "${threadId ?? ""}".`
274
+ `Captain chat session response ID "${session.id}" does not match active session "${approvalThreadId ?? ""}".`
250
275
  );
251
276
  }
252
277
  setMessages(session.messages);
253
278
  } catch (cause) {
279
+ if (activeThreadRef.current !== approvalThreadId) return;
254
280
  setApprovalError(
255
281
  cause instanceof Error ? cause : new Error(String(cause))
256
282
  );
@@ -266,6 +292,7 @@ function Chat({
266
292
  selectedModel,
267
293
  usage,
268
294
  threadId,
295
+ locked: runtimeLocked,
269
296
  onRuntimeChange: handleRuntimeChange
270
297
  }
271
298
  );
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.js","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type ChatTransport, type UIMessage } from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport { getChatSession, postToolApproval } from \"./approval\";\nimport {\n hasStructuredRuntime,\n resolveChatRuntime,\n selectedChatModel,\n} from \"./Chat.runtime\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport { toToolRenderRegistry } from \"./tool-render/registry\";\nimport { ToolRenderRegistryProvider } from \"./tool-render/context\";\nimport { ChatRuntimeToolbar } from \"./ChatRuntimeToolbar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport type {\n ToolRenderAdapter,\n ToolRenderRegistry,\n} from \"./tool-render/adapter\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolMeta,\n ToolResultRenderer,\n} from \"./types\";\nimport { usageSnapshotFromMetadata } from \"./usage-snapshot\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Controlled selected model id. */\n model?: string;\n /** Initially selected structured runtime. */\n defaultRuntime?: ChatModelRuntime;\n /** Controlled structured runtime. */\n runtime?: ChatModelRuntime;\n /** Runtime families and their host availability. */\n runtimeFamilies?: SpecRuntimeFamily[];\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Controlled selected reasoning effort (\"\" = none). */\n reasoningEffort?: string;\n /** Sampling temperature forwarded to the backend when set. */\n temperature?: number;\n /** Per-request budget forwarded to the backend when set. */\n budget?: ChatBudgetConfig;\n /** Claude Agent SDK permission mode forwarded to the backend when set. */\n permissionMode?: ClaudePermissionMode;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified when the user changes any runtime field. */\n onRuntimeChange?: (runtime: ChatModelRuntime) => void;\n /** Notified when the user changes reasoning effort. */\n onReasoningEffortChange?: (effort: string) => void;\n /** Notified when the user changes Claude permission mode. */\n onPermissionModeChange?: (mode: ClaudePermissionMode) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** Upload endpoint used before an attachment is added to a chat message. */\n attachmentsApi?: string;\n /** Custom durable upload adapter; overrides attachmentsApi. */\n attachmentUpload?: AttachmentUploadAdapter;\n attachmentLimits?: AttachmentLimits;\n /** Captain thread id used as the AI SDK chat id and forwarded in the body. */\n threadId?: string;\n /** Canonical Captain session endpoint used to hydrate messages and resolve\n * approvals. The session and approval ids are appended to this URL. */\n sessionsApi?: string | null;\n /** Optional host renderer for recognized completed tool outputs. Takes\n * priority over `toolRenderers` on the output surface. */\n renderToolResult?: ToolResultRenderer;\n /** Tool catalog, used to resolve each call's input/output JSON Schema so\n * params and results render with real labels instead of raw JSON. */\n tools?: ToolMeta[];\n /** Domain tool renderers. Host adapters are matched before the built-in\n * heuristics, which stay as the floor. */\n toolRenderers?: ToolRenderAdapter[] | ToolRenderRegistry;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n /** Prompt to send automatically once. A new `id` sends even when text repeats. */\n initialPrompt?: { id: number; text: string } | null;\n /** Called after `initialPrompt` has been handed to the chat transport. */\n onInitialPromptSent?: () => void;\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, permission mode and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n model: controlledModel,\n defaultRuntime,\n runtime: controlledRuntime,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort,\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onRuntimeChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n sessionsApi = null,\n renderToolResult,\n tools,\n toolRenderers,\n body,\n transport,\n initialMessages,\n initialPrompt,\n onInitialPromptSent,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [internalRuntime, setInternalRuntime] = useState<ChatModelRuntime>(\n () =>\n controlledRuntime ??\n resolveChatRuntime({\n models: modelsProp ?? [],\n current: defaultRuntime,\n preferredModel:\n controlledModel ??\n (defaultRuntime\n ? undefined\n : (defaultModel ?? defaultChatModelId(modelsProp ?? []))),\n effort: controlledEffort ?? defaultReasoningEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const [approvalError, setApprovalError] = useState<Error | undefined>();\n const [hydratedSessionId, setHydratedSessionId] = useState<string | null>(\n null,\n );\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n const runtime = controlledRuntime ?? internalRuntime;\n\n // Provided once here; <ToolCall> reads it from context, so no tool-render\n // props thread through Conversation/Message.\n const toolRegistry = useMemo(\n () => toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),\n [toolRenderers, tools],\n );\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n }, [modelsProp]);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) =>\n r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`)),\n )\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n })\n .catch((err) =>\n console.warn(\"clicky-ui: failed to load chat models\", err),\n );\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (controlledRuntime !== undefined) return;\n const defaultChanged =\n Boolean(defaultModel) && defaultModel !== lastDefaultModel.current;\n lastDefaultModel.current = defaultModel;\n setInternalRuntime((current) =>\n resolveChatRuntime({\n models,\n current,\n preferredModel:\n controlledModel ??\n (defaultChanged\n ? defaultModel\n : (selectedChatModel(models, current)?.id ??\n defaultModel ??\n defaultChatModelId(models))),\n effort: controlledEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n }, [\n controlledEffort,\n controlledModel,\n controlledRuntime,\n defaultModel,\n models,\n reasoningEfforts,\n temperature,\n ]);\n\n const selectedModel = selectedChatModel(models, runtime);\n const structuredRuntime = hasStructuredRuntime(runtime, selectedModel);\n const resolvedAttachmentUpload = useMemo(\n () =>\n attachmentUpload ??\n createAttachmentUploadAdapter({ endpoint: attachmentsApi }),\n [attachmentUpload, attachmentsApi],\n );\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/runtime selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(structuredRuntime\n ? { runtime }\n : {\n ...(runtime.model ? { model: runtime.model } : {}),\n ...(runtime.effort ? { reasoningEffort: runtime.effort } : {}),\n ...(runtime.temperature !== undefined\n ? { temperature: runtime.temperature }\n : {}),\n }),\n ...(budget && (budget.cost !== undefined || budget.maxTokens !== undefined)\n ? { budget }\n : {}),\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () =>\n transport ??\n new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const {\n messages,\n setMessages,\n sendMessage,\n regenerate,\n status,\n error,\n clearError,\n stop,\n } = useChat<ChatUIMessage>({\n transport: resolvedTransport,\n ...(threadId ? { id: threadId } : {}),\n ...(initialMessages\n ? { messages: initialMessages as ChatUIMessage[] }\n : {}),\n });\n const setMessagesRef = useRef(setMessages);\n setMessagesRef.current = setMessages;\n\n useEffect(() => {\n if (!sessionsApi || !threadId) return;\n let cancelled = false;\n setHydratedSessionId(null);\n setApprovalError(undefined);\n setMessagesRef.current([]);\n void getChatSession(sessionsApi, threadId)\n .then((session) => {\n if (cancelled) return;\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match requested session \"${threadId}\".`,\n );\n }\n setMessagesRef.current(session.messages as ChatUIMessage[]);\n setHydratedSessionId(session.id);\n })\n .catch((cause) => {\n if (cancelled) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [sessionsApi, threadId]);\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") return;\n if (sessionsApi && threadId && hydratedSessionId !== threadId) return;\n if (sentInitialPromptId.current === initialPrompt.id) return;\n const text = initialPrompt.text.trim();\n if (!text) return;\n sentInitialPromptId.current = initialPrompt.id;\n void sendMessage({ text });\n onInitialPromptSent?.();\n }, [\n hydratedSessionId,\n initialPrompt,\n onInitialPromptSent,\n sendMessage,\n sessionsApi,\n status,\n threadId,\n ]);\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) return;\n const snapshot = usageSnapshotFromMetadata(meta, {\n contextWindow: selectedModel?.contextWindow,\n modelLabel: selectedModel?.label,\n messageCount: messages.length,\n });\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n if (controlledRuntime === undefined) setInternalRuntime(next);\n onRuntimeChange?.(next);\n const nextModel = selectedChatModel(models, next);\n if (nextModel?.id !== selectedModel?.id) {\n onModelChange?.(nextModel?.id ?? next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (runtime.effort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n const resolveToolApproval = async (\n id: string,\n approved: boolean,\n reason?: string,\n ) => {\n setApprovalError(undefined);\n try {\n if (!sessionsApi) {\n throw new Error(\n \"A Captain sessions API is required to resolve tool approvals.\",\n );\n }\n const session = await postToolApproval({\n sessionsApi,\n sessionId: threadId ?? \"\",\n approvalId: id,\n approved,\n ...(reason ? { reason } : {}),\n });\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match active session \"${threadId ?? \"\"}\".`,\n );\n }\n setMessages(session.messages as ChatUIMessage[]);\n } catch (cause) {\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n };\n\n const toolbar = (\n <ChatRuntimeToolbar\n models={models}\n runtime={runtime}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n selectedModel={selectedModel}\n usage={usage}\n threadId={threadId}\n onRuntimeChange={handleRuntimeChange}\n />\n );\n\n const empty =\n messages.length === 0 && (emptyState || suggestions?.length) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions\n suggestions={suggestions}\n onSelect={(text) => void sendMessage({ text })}\n />\n )}\n </div>\n ) : undefined;\n\n return (\n <ToolRenderRegistryProvider value={toolRegistry}>\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={approvalError ?? error}\n onClearError={() => {\n setApprovalError(undefined);\n clearError();\n }}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(selectedModel?.id\n ? { model: selectedModel.id }\n : runtime.model\n ? { model: runtime.model }\n : {})}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void resolveToolApproval(id, approved, reason)\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n attachmentUpload={resolvedAttachmentUpload}\n {...(selectedModel?.inputMediaTypes\n ? { acceptedMediaTypes: selectedModel.inputMediaTypes }\n : {})}\n {...(attachmentLimits ? { attachmentLimits } : {})}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n </ToolRenderRegistryProvider>\n );\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Chat.js","sources":["../../../src/data/chat/Chat.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useState } from \"react\";\nimport { useChat } from \"@ai-sdk/react\";\nimport { DefaultChatTransport, type ChatTransport, type UIMessage } from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport {\n getChatSession,\n postToolApproval,\n type CaptainChatSession,\n} from \"./approval\";\nimport { isForkSeedMessage } from \"./fork-seed\";\nimport {\n hasStructuredRuntime,\n resolveChatRuntime,\n selectedChatModel,\n} from \"./Chat.runtime\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport { toToolRenderRegistry } from \"./tool-render/registry\";\nimport { ToolRenderRegistryProvider } from \"./tool-render/context\";\nimport { ChatRuntimeToolbar } from \"./ChatRuntimeToolbar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\nimport type {\n ToolRenderAdapter,\n ToolRenderRegistry,\n} from \"./tool-render/adapter\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatModelRuntime,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolMeta,\n ToolResultRenderer,\n} from \"./types\";\nimport { usageSnapshotFromMetadata } from \"./usage-snapshot\";\n\n/** Assistant messages carry token usage + cost the backend rode on the finish\n * part's `messageMetadata`. */\ntype ChatUIMessage = UIMessage<ChatMessageMetadata>;\n\nexport type ChatProps = {\n /** Endpoint that speaks the AI SDK v6 UI Message Stream protocol.\n * Ignored when `transport` is supplied. Defaults to \"/api/chat\". */\n api?: string;\n /** Model menu. When omitted and `modelsApi` is set, it is fetched. */\n models?: ChatModel[];\n /** Endpoint returning the model menu (ModelInfo[]). Defaults to\n * \"/api/chat/models\". Set to null to disable fetching. */\n modelsApi?: string | null;\n /** Initially selected model id; otherwise the first configured model. */\n defaultModel?: string;\n /** Controlled selected model id. */\n model?: string;\n /** Initially selected structured runtime. */\n defaultRuntime?: ChatModelRuntime;\n /** Controlled structured runtime. */\n runtime?: ChatModelRuntime;\n /** Runtime families and their host availability. */\n runtimeFamilies?: SpecRuntimeFamily[];\n /** Reasoning-effort options for capable models. */\n reasoningEfforts?: string[];\n /** Initially selected reasoning effort (\"\" = none). */\n defaultReasoningEffort?: string;\n /** Controlled selected reasoning effort (\"\" = none). */\n reasoningEffort?: string;\n /** Sampling temperature forwarded to the backend when set. */\n temperature?: number;\n /** Per-request budget forwarded to the backend when set. */\n budget?: ChatBudgetConfig;\n /** Claude Agent SDK permission mode forwarded to the backend when set. */\n permissionMode?: ClaudePermissionMode;\n /** Notified when the user changes the model. */\n onModelChange?: (id: string) => void;\n /** Notified when the user changes any runtime field. */\n onRuntimeChange?: (runtime: ChatModelRuntime) => void;\n /** Notified when the user changes reasoning effort. */\n onReasoningEffortChange?: (effort: string) => void;\n /** Notified when the user changes Claude permission mode. */\n onPermissionModeChange?: (mode: ClaudePermissionMode) => void;\n /** Notified after each assistant turn with a usage snapshot (tokens used out\n * of the model's context window + cumulative cost), for a usage gauge. */\n onUsage?: (usage: ChatUsageSummary) => void;\n /** Suggested prompts shown on the empty state. */\n suggestions?: Suggestion[];\n /** Enables file/image attachments. */\n enableAttachments?: boolean;\n /** Upload endpoint used before an attachment is added to a chat message. */\n attachmentsApi?: string;\n /** Custom durable upload adapter; overrides attachmentsApi. */\n attachmentUpload?: AttachmentUploadAdapter;\n attachmentLimits?: AttachmentLimits;\n /** Captain thread id used as the AI SDK chat id and forwarded in the body. */\n threadId?: string;\n /** Canonical Captain session endpoint used to hydrate messages and resolve\n * approvals. The session and approval ids are appended to this URL. */\n sessionsApi?: string | null;\n /** Prevents changing model/backend for an existing Captain session. Effort\n * and other per-turn generation settings remain editable. */\n runtimeLocked?: boolean;\n /** Reports the number of real (non-fork-seed) persisted/UI messages. */\n onMessageCountChange?: (count: number) => void;\n /** Reports the authoritative Captain session after detail hydration. */\n onSessionHydrated?: (session: CaptainChatSession) => void;\n /** Optional host renderer for recognized completed tool outputs. Takes\n * priority over `toolRenderers` on the output surface. */\n renderToolResult?: ToolResultRenderer;\n /** Tool catalog, used to resolve each call's input/output JSON Schema so\n * params and results render with real labels instead of raw JSON. */\n tools?: ToolMeta[];\n /** Domain tool renderers. Host adapters are matched before the built-in\n * heuristics, which stay as the floor. */\n toolRenderers?: ToolRenderAdapter[] | ToolRenderRegistry;\n /** Extra fields merged into every request body. */\n body?: Record<string, unknown>;\n /** Pre-built transport (e.g. a mock for stories/tests). */\n transport?: ChatTransport<UIMessage>;\n /** Initial messages to seed the conversation. */\n initialMessages?: UIMessage[];\n /** Prompt to send automatically once. A new `id` sends even when text repeats. */\n initialPrompt?: { id: number; text: string } | null;\n /** Called after `initialPrompt` has been handed to the chat transport. */\n onInitialPromptSent?: () => void;\n placeholder?: string;\n emptyState?: React.ReactNode;\n className?: string;\n};\n\n/** Self-contained AI chat over a v6-compatible `/api/chat`: a streaming\n * conversation with model/effort selectors, suggestions, attachments,\n * per-message copy/regenerate, and human-in-the-loop tool approvals. The\n * backend owns model selection and tool execution; the selected model,\n * effort, permission mode and thread id are forwarded in the request body. */\nexport function Chat({\n api = \"/api/chat\",\n models: modelsProp,\n modelsApi = \"/api/chat/models\",\n defaultModel,\n model: controlledModel,\n defaultRuntime,\n runtime: controlledRuntime,\n runtimeFamilies,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort,\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onRuntimeChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n sessionsApi = null,\n runtimeLocked = false,\n onMessageCountChange,\n onSessionHydrated,\n renderToolResult,\n tools,\n toolRenderers,\n body,\n transport,\n initialMessages,\n initialPrompt,\n onInitialPromptSent,\n placeholder,\n emptyState,\n className,\n}: ChatProps) {\n const [models, setModels] = useState<ChatModel[]>(modelsProp ?? []);\n const [internalRuntime, setInternalRuntime] = useState<ChatModelRuntime>(\n () =>\n controlledRuntime ??\n resolveChatRuntime({\n models: modelsProp ?? [],\n current: defaultRuntime,\n preferredModel:\n controlledModel ??\n (defaultRuntime\n ? undefined\n : (defaultModel ?? defaultChatModelId(modelsProp ?? []))),\n effort: controlledEffort ?? defaultReasoningEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const [approvalError, setApprovalError] = useState<Error | undefined>();\n const [hydratedSessionId, setHydratedSessionId] = useState<string | null>(\n null,\n );\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n const activeThreadRef = useRef(threadId);\n const onMessageCountChangeRef = useRef(onMessageCountChange);\n const onSessionHydratedRef = useRef(onSessionHydrated);\n activeThreadRef.current = threadId;\n onMessageCountChangeRef.current = onMessageCountChange;\n onSessionHydratedRef.current = onSessionHydrated;\n const runtime = controlledRuntime ?? internalRuntime;\n\n // Provided once here; <ToolCall> reads it from context, so no tool-render\n // props thread through Conversation/Message.\n const toolRegistry = useMemo(\n () => toToolRenderRegistry(toolRenderers, tools ? { tools } : {}),\n [toolRenderers, tools],\n );\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n }, [modelsProp]);\n\n // Fetch the model menu unless one was supplied or fetching is disabled.\n useEffect(() => {\n if (modelsProp || !modelsApi) return;\n let cancelled = false;\n fetch(modelsApi)\n .then((r) =>\n r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`)),\n )\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n })\n .catch((err) =>\n console.warn(\"clicky-ui: failed to load chat models\", err),\n );\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi]);\n\n useEffect(() => {\n if (controlledRuntime !== undefined) return;\n const defaultChanged =\n Boolean(defaultModel) && defaultModel !== lastDefaultModel.current;\n lastDefaultModel.current = defaultModel;\n setInternalRuntime((current) =>\n resolveChatRuntime({\n models,\n current,\n preferredModel:\n controlledModel ??\n (defaultChanged\n ? defaultModel\n : (selectedChatModel(models, current)?.id ??\n defaultModel ??\n defaultChatModelId(models))),\n effort: controlledEffort,\n temperature,\n reasoningEfforts,\n }),\n );\n }, [\n controlledEffort,\n controlledModel,\n controlledRuntime,\n defaultModel,\n models,\n reasoningEfforts,\n temperature,\n ]);\n\n const selectedModel = selectedChatModel(models, runtime);\n const structuredRuntime = hasStructuredRuntime(runtime, selectedModel);\n const resolvedAttachmentUpload = useMemo(\n () =>\n attachmentUpload ??\n createAttachmentUploadAdapter({ endpoint: attachmentsApi }),\n [attachmentUpload, attachmentsApi],\n );\n\n // A function body keeps the transport stable while always sending the latest\n // model/effort/runtime selections.\n const bodyRef = useRef<Record<string, unknown>>({});\n bodyRef.current = {\n ...body,\n ...(structuredRuntime\n ? { runtime }\n : {\n ...(runtime.model ? { model: runtime.model } : {}),\n ...(runtime.effort ? { reasoningEffort: runtime.effort } : {}),\n ...(runtime.temperature !== undefined\n ? { temperature: runtime.temperature }\n : {}),\n }),\n ...(budget && (budget.cost !== undefined || budget.maxTokens !== undefined)\n ? { budget }\n : {}),\n ...(permissionMode ? { permissionMode } : {}),\n ...(threadId ? { threadId } : {}),\n };\n\n const resolvedTransport = useMemo<ChatTransport<UIMessage>>(\n () =>\n transport ??\n new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const {\n messages,\n setMessages,\n sendMessage,\n regenerate,\n status,\n error,\n clearError,\n stop,\n } = useChat<ChatUIMessage>({\n transport: resolvedTransport,\n ...(threadId ? { id: threadId } : {}),\n ...(initialMessages\n ? { messages: initialMessages as ChatUIMessage[] }\n : {}),\n });\n const setMessagesRef = useRef(setMessages);\n setMessagesRef.current = setMessages;\n\n useEffect(() => {\n if (!sessionsApi || !threadId) return;\n let cancelled = false;\n setHydratedSessionId(null);\n setApprovalError(undefined);\n setMessagesRef.current([]);\n void getChatSession(sessionsApi, threadId)\n .then((session) => {\n if (cancelled) return;\n if (session.id !== threadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match requested session \"${threadId}\".`,\n );\n }\n setMessagesRef.current(session.messages as ChatUIMessage[]);\n setHydratedSessionId(session.id);\n onSessionHydratedRef.current?.(session);\n })\n .catch((cause) => {\n if (cancelled) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [sessionsApi, threadId]);\n\n useEffect(() => {\n onMessageCountChangeRef.current?.(\n messages.filter((message) => !isForkSeedMessage(message)).length,\n );\n }, [messages]);\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") return;\n if (sessionsApi && threadId && hydratedSessionId !== threadId) return;\n if (sentInitialPromptId.current === initialPrompt.id) return;\n const text = initialPrompt.text.trim();\n if (!text) return;\n sentInitialPromptId.current = initialPrompt.id;\n void sendMessage({ text });\n onInitialPromptSent?.();\n }, [\n hydratedSessionId,\n initialPrompt,\n onInitialPromptSent,\n sendMessage,\n sessionsApi,\n status,\n threadId,\n ]);\n\n // Surface a usage snapshot after each settled assistant turn. The backend\n // rides usage/cost on the finish part's messageMetadata; we read it off the\n // last assistant message and resolve maxTokens from the selected model.\n const onUsageRef = useRef(onUsage);\n onUsageRef.current = onUsage;\n useEffect(() => {\n if (status !== \"ready\") return;\n const last = [...messages].reverse().find((m) => m.role === \"assistant\");\n const meta = last?.metadata;\n if (!meta) {\n setUsage(null);\n return;\n }\n const settledModel = meta.model\n ? selectedChatModel(models, { id: meta.model, model: meta.model })\n : undefined;\n const snapshot = usageSnapshotFromMetadata(meta, {\n contextWindow: settledModel?.contextWindow,\n modelLabel: settledModel?.label,\n messageCount: messages.length,\n });\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, models, status]);\n\n const handleRuntimeChange = (next: ChatModelRuntime) => {\n if (controlledRuntime === undefined) setInternalRuntime(next);\n onRuntimeChange?.(next);\n const nextModel = selectedChatModel(models, next);\n if (nextModel?.id !== selectedModel?.id) {\n onModelChange?.(nextModel?.id ?? next.id ?? next.model ?? \"\");\n }\n if ((next.effort ?? \"\") !== (runtime.effort ?? \"\")) {\n onReasoningEffortChange?.(next.effort ?? \"\");\n }\n };\n\n const resolveToolApproval = async (\n id: string,\n approved: boolean,\n reason?: string,\n ) => {\n const approvalThreadId = threadId;\n setApprovalError(undefined);\n try {\n if (!sessionsApi) {\n throw new Error(\n \"A Captain sessions API is required to resolve tool approvals.\",\n );\n }\n const session = await postToolApproval({\n sessionsApi,\n sessionId: approvalThreadId ?? \"\",\n approvalId: id,\n approved,\n ...(reason ? { reason } : {}),\n });\n if (activeThreadRef.current !== approvalThreadId) return;\n if (session.id !== approvalThreadId) {\n throw new Error(\n `Captain chat session response ID \"${session.id}\" does not match active session \"${approvalThreadId ?? \"\"}\".`,\n );\n }\n setMessages(session.messages as ChatUIMessage[]);\n } catch (cause) {\n if (activeThreadRef.current !== approvalThreadId) return;\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n };\n\n const toolbar = (\n <ChatRuntimeToolbar\n models={models}\n runtime={runtime}\n runtimeFamilies={runtimeFamilies}\n reasoningEfforts={reasoningEfforts}\n selectedModel={selectedModel}\n usage={usage}\n threadId={threadId}\n locked={runtimeLocked}\n onRuntimeChange={handleRuntimeChange}\n />\n );\n\n const empty =\n messages.length === 0 && (emptyState || suggestions?.length) ? (\n <div className=\"flex flex-col items-center gap-4\">\n {emptyState}\n {suggestions && suggestions.length > 0 && (\n <Suggestions\n suggestions={suggestions}\n onSelect={(text) => void sendMessage({ text })}\n />\n )}\n </div>\n ) : undefined;\n\n return (\n <ToolRenderRegistryProvider value={toolRegistry}>\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={approvalError ?? error}\n onClearError={() => {\n setApprovalError(undefined);\n clearError();\n }}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(selectedModel?.id\n ? { model: selectedModel.id }\n : runtime.model\n ? { model: runtime.model }\n : {})}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void resolveToolApproval(id, approved, reason)\n }\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n <div className=\"p-4 pt-0\">\n <PromptInput\n status={status}\n onStop={() => void stop()}\n placeholder={placeholder}\n enableAttachments={enableAttachments}\n attachmentUpload={resolvedAttachmentUpload}\n {...(selectedModel?.inputMediaTypes\n ? { acceptedMediaTypes: selectedModel.inputMediaTypes }\n : {})}\n {...(attachmentLimits ? { attachmentLimits } : {})}\n toolbar={toolbar}\n onSubmit={(text, files) => void sendMessage({ text, files })}\n />\n </div>\n </div>\n </ToolRenderRegistryProvider>\n );\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;AA8IO,SAAS,KAAK;AAAA,EACnB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,SAAS;AAAA,EACT;AAAA,EACA,mBAAmB;AAAA,EACnB;AAAA,EACA,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;AACZ,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAsB,cAAc,CAAA,CAAE;AAClE,QAAM,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAC5C,MACE,qBACA,mBAAmB;AAAA,MACjB,QAAQ,cAAc,CAAA;AAAA,MACtB,SAAS;AAAA,MACT,gBACE,oBACC,iBACG,SACC,gBAAgB,mBAAmB,cAAc,CAAA,CAAE;AAAA,MAC1D,QAAQ,oBAAoB;AAAA,MAC5B;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EAAA;AAEL,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkC,IAAI;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAI,SAAA;AAC1C,QAAM,CAAC,mBAAmB,oBAAoB,IAAI;AAAA,IAChD;AAAA,EAAA;AAEF,QAAM,mBAAmB,OAAO,YAAY;AAC5C,QAAM,sBAAsB,OAAsB,IAAI;AACtD,QAAM,kBAAkB,OAAO,QAAQ;AACvC,QAAM,0BAA0B,OAAO,oBAAoB;AAC3D,QAAM,uBAAuB,OAAO,iBAAiB;AACrD,kBAAgB,UAAU;AAC1B,0BAAwB,UAAU;AAClC,uBAAqB,UAAU;AAC/B,QAAM,UAAU,qBAAqB;AAIrC,QAAM,eAAe;AAAA,IACnB,MAAM,qBAAqB,eAAe,QAAQ,EAAE,MAAA,IAAU,CAAA,CAAE;AAAA,IAChE,CAAC,eAAe,KAAK;AAAA,EAAA;AAGvB,YAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,cAAU,UAAU;AAAA,EACtB,GAAG,CAAC,UAAU,CAAC;AAGf,YAAU,MAAM;AACd,QAAI,cAAc,CAAC,UAAW;AAC9B,QAAI,YAAY;AAChB,UAAM,SAAS,EACZ;AAAA,MAAK,CAAC,MACL,EAAE,KAAK,EAAE,KAAA,IAAS,QAAQ,OAAO,IAAI,MAAM,UAAU,EAAE,MAAM,EAAE,CAAC;AAAA,IAAA,EAEjE,KAAK,CAAC,SAAsB;AAC3B,UAAI,UAAW;AACf,gBAAU,IAAI;AAAA,IAChB,CAAC,EACA;AAAA,MAAM,CAAC,QACN,QAAQ,KAAK,yCAAyC,GAAG;AAAA,IAAA;AAE7D,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,SAAS,CAAC;AAE1B,YAAU,MAAM;AACd,QAAI,sBAAsB,OAAW;AACrC,UAAM,iBACJ,QAAQ,YAAY,KAAK,iBAAiB,iBAAiB;AAC7D,qBAAiB,UAAU;AAC3B;AAAA,MAAmB,CAAC;;AAClB,kCAAmB;AAAA,UACjB;AAAA,UACA;AAAA,UACA,gBACE,oBACC,iBACG,iBACC,uBAAkB,QAAQ,OAAO,MAAjC,mBAAoC,OACrC,gBACA,mBAAmB,MAAM;AAAA,UAC/B,QAAQ;AAAA,UACR;AAAA,UACA;AAAA,QAAA,CACD;AAAA;AAAA,IAAA;AAAA,EAEL,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAED,QAAM,gBAAgB,kBAAkB,QAAQ,OAAO;AACvD,QAAM,oBAAoB,qBAAqB,SAAS,aAAa;AACrE,QAAM,2BAA2B;AAAA,IAC/B,MACE,oBACA,8BAA8B,EAAE,UAAU,gBAAgB;AAAA,IAC5D,CAAC,kBAAkB,cAAc;AAAA,EAAA;AAKnC,QAAM,UAAU,OAAgC,EAAE;AAClD,UAAQ,UAAU;AAAA,IAChB,GAAG;AAAA,IACH,GAAI,oBACA,EAAE,YACF;AAAA,MACE,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAA,IAAU,CAAA;AAAA,MAC/C,GAAI,QAAQ,SAAS,EAAE,iBAAiB,QAAQ,OAAA,IAAW,CAAA;AAAA,MAC3D,GAAI,QAAQ,gBAAgB,SACxB,EAAE,aAAa,QAAQ,gBACvB,CAAA;AAAA,IAAC;AAAA,IAEX,GAAI,WAAW,OAAO,SAAS,UAAa,OAAO,cAAc,UAC7D,EAAE,OAAA,IACF,CAAA;AAAA,IACJ,GAAI,iBAAiB,EAAE,eAAA,IAAmB,CAAA;AAAA,IAC1C,GAAI,WAAW,EAAE,aAAa,CAAA;AAAA,EAAC;AAGjC,QAAM,oBAAoB;AAAA,IACxB,MACE,aACA,IAAI,qBAAqB,EAAE,KAAK,MAAM,MAAM,QAAQ,SAAS;AAAA,IAC/D,CAAC,WAAW,GAAG;AAAA,EAAA;AAGjB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,IACE,QAAuB;AAAA,IACzB,WAAW;AAAA,IACX,GAAI,WAAW,EAAE,IAAI,SAAA,IAAa,CAAA;AAAA,IAClC,GAAI,kBACA,EAAE,UAAU,oBACZ,CAAA;AAAA,EAAC,CACN;AACD,QAAM,iBAAiB,OAAO,WAAW;AACzC,iBAAe,UAAU;AAEzB,YAAU,MAAM;AACd,QAAI,CAAC,eAAe,CAAC,SAAU;AAC/B,QAAI,YAAY;AAChB,yBAAqB,IAAI;AACzB,qBAAiB,MAAS;AAC1B,mBAAe,QAAQ,EAAE;AACzB,SAAK,eAAe,aAAa,QAAQ,EACtC,KAAK,CAAC,YAAY;;AACjB,UAAI,UAAW;AACf,UAAI,QAAQ,OAAO,UAAU;AAC3B,cAAM,IAAI;AAAA,UACR,qCAAqC,QAAQ,EAAE,uCAAuC,QAAQ;AAAA,QAAA;AAAA,MAElG;AACA,qBAAe,QAAQ,QAAQ,QAA2B;AAC1D,2BAAqB,QAAQ,EAAE;AAC/B,iCAAqB,YAArB,8CAA+B;AAAA,IACjC,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,UAAI,UAAW;AACf;AAAA,QACE,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MAAA;AAAA,IAE5D,CAAC;AACH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,aAAa,QAAQ,CAAC;AAE1B,YAAU,MAAM;;AACd,kCAAwB,YAAxB;AAAA;AAAA,MACE,SAAS,OAAO,CAAC,YAAY,CAAC,kBAAkB,OAAO,CAAC,EAAE;AAAA;AAAA,EAE9D,GAAG,CAAC,QAAQ,CAAC;AAEb,YAAU,MAAM;AACd,QAAI,CAAC,iBAAiB,WAAW,QAAS;AAC1C,QAAI,eAAe,YAAY,sBAAsB,SAAU;AAC/D,QAAI,oBAAoB,YAAY,cAAc,GAAI;AACtD,UAAM,OAAO,cAAc,KAAK,KAAA;AAChC,QAAI,CAAC,KAAM;AACX,wBAAoB,UAAU,cAAc;AAC5C,SAAK,YAAY,EAAE,MAAM;AACzB;AAAA,EACF,GAAG;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA,CACD;AAKD,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,YAAU,MAAM;;AACd,QAAI,WAAW,QAAS;AACxB,UAAM,OAAO,CAAC,GAAG,QAAQ,EAAE,QAAA,EAAU,KAAK,CAAC,MAAM,EAAE,SAAS,WAAW;AACvE,UAAM,OAAO,6BAAM;AACnB,QAAI,CAAC,MAAM;AACT,eAAS,IAAI;AACb;AAAA,IACF;AACA,UAAM,eAAe,KAAK,QACtB,kBAAkB,QAAQ,EAAE,IAAI,KAAK,OAAO,OAAO,KAAK,MAAA,CAAO,IAC/D;AACJ,UAAM,WAAW,0BAA0B,MAAM;AAAA,MAC/C,eAAe,6CAAc;AAAA,MAC7B,YAAY,6CAAc;AAAA,MAC1B,cAAc,SAAS;AAAA,IAAA,CACxB;AACD,aAAS,QAAQ;AACjB,qBAAW,YAAX,oCAAqB;AAAA,EACvB,GAAG,CAAC,UAAU,QAAQ,MAAM,CAAC;AAE7B,QAAM,sBAAsB,CAAC,SAA2B;AACtD,QAAI,sBAAsB,OAAW,oBAAmB,IAAI;AAC5D,uDAAkB;AAClB,UAAM,YAAY,kBAAkB,QAAQ,IAAI;AAChD,SAAI,uCAAW,SAAO,+CAAe,KAAI;AACvC,sDAAgB,uCAAW,OAAM,KAAK,MAAM,KAAK,SAAS;AAAA,IAC5D;AACA,SAAK,KAAK,UAAU,SAAS,QAAQ,UAAU,KAAK;AAClD,yEAA0B,KAAK,UAAU;AAAA,IAC3C;AAAA,EACF;AAEA,QAAM,sBAAsB,OAC1B,IACA,UACA,WACG;AACH,UAAM,mBAAmB;AACzB,qBAAiB,MAAS;AAC1B,QAAI;AACF,UAAI,CAAC,aAAa;AAChB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAAA,MAEJ;AACA,YAAM,UAAU,MAAM,iBAAiB;AAAA,QACrC;AAAA,QACA,WAAW,oBAAoB;AAAA,QAC/B,YAAY;AAAA,QACZ;AAAA,QACA,GAAI,SAAS,EAAE,WAAW,CAAA;AAAA,MAAC,CAC5B;AACD,UAAI,gBAAgB,YAAY,iBAAkB;AAClD,UAAI,QAAQ,OAAO,kBAAkB;AACnC,cAAM,IAAI;AAAA,UACR,qCAAqC,QAAQ,EAAE,oCAAoC,oBAAoB,EAAE;AAAA,QAAA;AAAA,MAE7G;AACA,kBAAY,QAAQ,QAA2B;AAAA,IACjD,SAAS,OAAO;AACd,UAAI,gBAAgB,YAAY,iBAAkB;AAClD;AAAA,QACE,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MAAA;AAAA,IAE5D;AAAA,EACF;AAEA,QAAM,UACJ;AAAA,IAAC;AAAA,IAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,iBAAiB;AAAA,IAAA;AAAA,EAAA;AAIrB,QAAM,QACJ,SAAS,WAAW,MAAM,eAAc,2CAAa,WACnD,qBAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,SAAS,KACnC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,UAAU,CAAC,SAAS,KAAK,YAAY,EAAE,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAC/C,EAAA,CAEJ,IACE;AAEN,SACE,oBAAC,4BAAA,EAA2B,OAAO,cACjC,UAAA,qBAAC,SAAI,WAAW,GAAG,wBAAwB,SAAS,GAClD,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA,OAAO,iBAAiB;AAAA,QACxB,cAAc,MAAM;AAClB,2BAAiB,MAAS;AAC1B,qBAAA;AAAA,QACF;AAAA,QACC,GAAI,WAAW,EAAE,WAAW,SAAA,IAAa,CAAA;AAAA,QACzC,IAAI,+CAAe,MAChB,EAAE,OAAO,cAAc,GAAA,IACvB,QAAQ,QACN,EAAE,OAAO,QAAQ,MAAA,IACjB,CAAA;AAAA,QACN,YAAY;AAAA,QACZ,cAAc,CAAC,cAAc,KAAK,WAAW,EAAE,WAAW;AAAA,QAC1D,WAAW,CAAC,IAAI,UAAU,WACxB,KAAK,oBAAoB,IAAI,UAAU,MAAM;AAAA,QAE9C,GAAI,mBAAmB,EAAE,qBAAqB,CAAA;AAAA,MAAC;AAAA,IAAA;AAAA,IAElD,oBAAC,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC;AAAA,QACA,QAAQ,MAAM,KAAK,KAAA;AAAA,QACnB;AAAA,QACA;AAAA,QACA,kBAAkB;AAAA,QACjB,IAAI,+CAAe,mBAChB,EAAE,oBAAoB,cAAc,gBAAA,IACpC,CAAA;AAAA,QACH,GAAI,mBAAmB,EAAE,iBAAA,IAAqB,CAAA;AAAA,QAC/C;AAAA,QACA,UAAU,CAAC,MAAM,UAAU,KAAK,YAAY,EAAE,MAAM,MAAA,CAAO;AAAA,MAAA;AAAA,IAAA,EAC7D,CACF;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}