@flanksource/clicky-ui 0.3.16 → 0.3.17

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 (158) hide show
  1. package/dist/data/DataTable.cjs +30 -10
  2. package/dist/data/DataTable.cjs.map +1 -1
  3. package/dist/data/DataTable.d.ts +2 -0
  4. package/dist/data/DataTable.d.ts.map +1 -1
  5. package/dist/data/DataTable.js +30 -10
  6. package/dist/data/DataTable.js.map +1 -1
  7. package/dist/data/ServerTimingBadge.cjs +64 -0
  8. package/dist/data/ServerTimingBadge.cjs.map +1 -0
  9. package/dist/data/ServerTimingBadge.d.ts +9 -0
  10. package/dist/data/ServerTimingBadge.d.ts.map +1 -0
  11. package/dist/data/ServerTimingBadge.js +64 -0
  12. package/dist/data/ServerTimingBadge.js.map +1 -0
  13. package/dist/data/ai/ChatWindow.cjs +9 -1
  14. package/dist/data/ai/ChatWindow.cjs.map +1 -1
  15. package/dist/data/ai/ChatWindow.d.ts.map +1 -1
  16. package/dist/data/ai/ChatWindow.js +9 -1
  17. package/dist/data/ai/ChatWindow.js.map +1 -1
  18. package/dist/data/ai/ChatWindow.thread-status.cjs +14 -0
  19. package/dist/data/ai/ChatWindow.thread-status.cjs.map +1 -0
  20. package/dist/data/ai/ChatWindow.thread-status.d.ts +5 -0
  21. package/dist/data/ai/ChatWindow.thread-status.d.ts.map +1 -0
  22. package/dist/data/ai/ChatWindow.thread-status.js +14 -0
  23. package/dist/data/ai/ChatWindow.thread-status.js.map +1 -0
  24. package/dist/data/ai/ChatWindow.thread.cjs +68 -0
  25. package/dist/data/ai/ChatWindow.thread.cjs.map +1 -0
  26. package/dist/data/ai/ChatWindow.thread.d.ts +16 -0
  27. package/dist/data/ai/ChatWindow.thread.d.ts.map +1 -0
  28. package/dist/data/ai/ChatWindow.thread.js +68 -0
  29. package/dist/data/ai/ChatWindow.thread.js.map +1 -0
  30. package/dist/data/ai/PromptRunEditor/index.cjs +100 -88
  31. package/dist/data/ai/PromptRunEditor/index.cjs.map +1 -1
  32. package/dist/data/ai/PromptRunEditor/index.d.ts +5 -9
  33. package/dist/data/ai/PromptRunEditor/index.d.ts.map +1 -1
  34. package/dist/data/ai/PromptRunEditor/index.js +103 -91
  35. package/dist/data/ai/PromptRunEditor/index.js.map +1 -1
  36. package/dist/data/ai/PromptRunEditor/model.cjs +47 -0
  37. package/dist/data/ai/PromptRunEditor/model.cjs.map +1 -0
  38. package/dist/data/ai/PromptRunEditor/model.d.ts +17 -0
  39. package/dist/data/ai/PromptRunEditor/model.d.ts.map +1 -0
  40. package/dist/data/ai/PromptRunEditor/model.js +47 -0
  41. package/dist/data/ai/PromptRunEditor/model.js.map +1 -0
  42. package/dist/data/ai/RuntimeBar.cjs +104 -32
  43. package/dist/data/ai/RuntimeBar.cjs.map +1 -1
  44. package/dist/data/ai/RuntimeBar.d.ts +4 -11
  45. package/dist/data/ai/RuntimeBar.d.ts.map +1 -1
  46. package/dist/data/ai/RuntimeBar.js +107 -35
  47. package/dist/data/ai/RuntimeBar.js.map +1 -1
  48. package/dist/data/ai/RuntimeBar.model.cjs +14 -0
  49. package/dist/data/ai/RuntimeBar.model.cjs.map +1 -0
  50. package/dist/data/ai/RuntimeBar.model.d.ts +10 -0
  51. package/dist/data/ai/RuntimeBar.model.d.ts.map +1 -0
  52. package/dist/data/ai/RuntimeBar.model.js +14 -0
  53. package/dist/data/ai/RuntimeBar.model.js.map +1 -0
  54. package/dist/data/ai/RuntimeBarCombo.cjs +359 -0
  55. package/dist/data/ai/RuntimeBarCombo.cjs.map +1 -0
  56. package/dist/data/ai/RuntimeBarCombo.d.ts +22 -0
  57. package/dist/data/ai/RuntimeBarCombo.d.ts.map +1 -0
  58. package/dist/data/ai/RuntimeBarCombo.js +359 -0
  59. package/dist/data/ai/RuntimeBarCombo.js.map +1 -0
  60. package/dist/data/ai/SessionViewer.header.cjs +17 -9
  61. package/dist/data/ai/SessionViewer.header.cjs.map +1 -1
  62. package/dist/data/ai/SessionViewer.header.d.ts.map +1 -1
  63. package/dist/data/ai/SessionViewer.header.js +17 -9
  64. package/dist/data/ai/SessionViewer.header.js.map +1 -1
  65. package/dist/data/ai/SessionViewer.model.cjs +2 -0
  66. package/dist/data/ai/SessionViewer.model.cjs.map +1 -1
  67. package/dist/data/ai/SessionViewer.model.d.ts +4 -0
  68. package/dist/data/ai/SessionViewer.model.d.ts.map +1 -1
  69. package/dist/data/ai/SessionViewer.model.js +2 -0
  70. package/dist/data/ai/SessionViewer.model.js.map +1 -1
  71. package/dist/data/ai/SessionViewer.unified.d.ts +1 -0
  72. package/dist/data/ai/SessionViewer.unified.d.ts.map +1 -1
  73. package/dist/data/ai/SpecRuntimeEditor/fields.js +1 -1
  74. package/dist/data/ai/ThreadPicker.cjs.map +1 -1
  75. package/dist/data/ai/ThreadPicker.d.ts +2 -0
  76. package/dist/data/ai/ThreadPicker.d.ts.map +1 -1
  77. package/dist/data/ai/ThreadPicker.js.map +1 -1
  78. package/dist/data/ai/index.d.ts +1 -0
  79. package/dist/data/ai/index.d.ts.map +1 -1
  80. package/dist/data/ai/model-capabilities.cjs +12 -1
  81. package/dist/data/ai/model-capabilities.cjs.map +1 -1
  82. package/dist/data/ai/model-capabilities.d.ts +2 -6
  83. package/dist/data/ai/model-capabilities.d.ts.map +1 -1
  84. package/dist/data/ai/model-capabilities.js +12 -1
  85. package/dist/data/ai/model-capabilities.js.map +1 -1
  86. package/dist/data/ai/runtime-mode.cjs +10 -1
  87. package/dist/data/ai/runtime-mode.cjs.map +1 -1
  88. package/dist/data/ai/runtime-mode.d.ts +1 -0
  89. package/dist/data/ai/runtime-mode.d.ts.map +1 -1
  90. package/dist/data/ai/runtime-mode.js +11 -2
  91. package/dist/data/ai/runtime-mode.js.map +1 -1
  92. package/dist/data/chat/Chat.cjs +90 -21
  93. package/dist/data/chat/Chat.cjs.map +1 -1
  94. package/dist/data/chat/Chat.d.ts +4 -1
  95. package/dist/data/chat/Chat.d.ts.map +1 -1
  96. package/dist/data/chat/Chat.js +90 -21
  97. package/dist/data/chat/Chat.js.map +1 -1
  98. package/dist/data/chat/ContextMeter.cjs +144 -73
  99. package/dist/data/chat/ContextMeter.cjs.map +1 -1
  100. package/dist/data/chat/ContextMeter.d.ts +8 -3
  101. package/dist/data/chat/ContextMeter.d.ts.map +1 -1
  102. package/dist/data/chat/ContextMeter.js +144 -73
  103. package/dist/data/chat/ContextMeter.js.map +1 -1
  104. package/dist/data/chat/approval.cjs +21 -0
  105. package/dist/data/chat/approval.cjs.map +1 -0
  106. package/dist/data/chat/approval.d.ts +11 -0
  107. package/dist/data/chat/approval.d.ts.map +1 -0
  108. package/dist/data/chat/approval.js +21 -0
  109. package/dist/data/chat/approval.js.map +1 -0
  110. package/dist/data/chat/types.cjs.map +1 -1
  111. package/dist/data/chat/types.d.ts +12 -0
  112. package/dist/data/chat/types.d.ts.map +1 -1
  113. package/dist/data/chat/types.js.map +1 -1
  114. package/dist/data/server-timing.cjs +120 -0
  115. package/dist/data/server-timing.cjs.map +1 -0
  116. package/dist/data/server-timing.d.ts +11 -0
  117. package/dist/data/server-timing.d.ts.map +1 -0
  118. package/dist/data/server-timing.js +120 -0
  119. package/dist/data/server-timing.js.map +1 -0
  120. package/dist/data/version-info.cjs +3 -3
  121. package/dist/data/version-info.js +3 -3
  122. package/dist/data.cjs +7 -0
  123. package/dist/data.cjs.map +1 -1
  124. package/dist/data.d.ts +2 -0
  125. package/dist/data.d.ts.map +1 -1
  126. package/dist/data.js +7 -0
  127. package/dist/data.js.map +1 -1
  128. package/dist/index.cjs +7 -0
  129. package/dist/index.cjs.map +1 -1
  130. package/dist/index.js +7 -0
  131. package/dist/index.js.map +1 -1
  132. package/dist/layout/AppShell.cjs +1 -1
  133. package/dist/layout/AppShell.cjs.map +1 -1
  134. package/dist/layout/AppShell.js +1 -1
  135. package/dist/layout/AppShell.js.map +1 -1
  136. package/dist/overlay/HoverCard.cjs.map +1 -1
  137. package/dist/overlay/HoverCard.d.ts +2 -2
  138. package/dist/overlay/HoverCard.d.ts.map +1 -1
  139. package/dist/overlay/HoverCard.js.map +1 -1
  140. package/dist/rpc/OperationCatalog.cjs +28 -51
  141. package/dist/rpc/OperationCatalog.cjs.map +1 -1
  142. package/dist/rpc/OperationCatalog.d.ts +1 -6
  143. package/dist/rpc/OperationCatalog.d.ts.map +1 -1
  144. package/dist/rpc/OperationCatalog.js +29 -52
  145. package/dist/rpc/OperationCatalog.js.map +1 -1
  146. package/dist/rpc/OperationResultView.cjs +42 -0
  147. package/dist/rpc/OperationResultView.cjs.map +1 -1
  148. package/dist/rpc/OperationResultView.d.ts +2 -1
  149. package/dist/rpc/OperationResultView.d.ts.map +1 -1
  150. package/dist/rpc/OperationResultView.js +42 -0
  151. package/dist/rpc/OperationResultView.js.map +1 -1
  152. package/dist/styles.css +1 -1
  153. package/dist/tailwind-preset.cjs +4 -0
  154. package/dist/tailwind-preset.cjs.map +1 -1
  155. package/dist/tailwind-preset.d.ts.map +1 -1
  156. package/dist/tailwind-preset.js +4 -0
  157. package/dist/tailwind-preset.js.map +1 -1
  158. package/package.json +1 -1
@@ -13,6 +13,7 @@ const providerIcons = require("./provider-icons.cjs");
13
13
  const ContextMeter = require("./ContextMeter.cjs");
14
14
  const effortIcons = require("./effort-icons.cjs");
15
15
  const models = require("./models.cjs");
16
+ const approval = require("./approval.cjs");
16
17
  const attachmentUpload = require("./attachment-upload.cjs");
17
18
  function Chat({
18
19
  api = "/api/chat",
@@ -35,6 +36,7 @@ function Chat({
35
36
  attachmentUpload: attachmentUpload$1,
36
37
  attachmentLimits,
37
38
  threadId,
39
+ approvalApi = null,
38
40
  renderToolResult,
39
41
  body,
40
42
  transport,
@@ -45,10 +47,16 @@ function Chat({
45
47
  emptyState,
46
48
  className
47
49
  }) {
50
+ var _a;
48
51
  const [models$1, setModels] = react.useState(modelsProp ?? []);
49
- const [model, setModel] = react.useState(controlledModel ?? defaultModel);
50
- const [effort, setEffort] = react.useState(controlledEffort ?? defaultReasoningEffort);
52
+ const [model, setModel] = react.useState(
53
+ controlledModel ?? defaultModel
54
+ );
55
+ const [effort, setEffort] = react.useState(
56
+ controlledEffort ?? defaultReasoningEffort
57
+ );
51
58
  const [usage, setUsage] = react.useState(null);
59
+ const [approvalError, setApprovalError] = react.useState();
52
60
  const lastDefaultModel = react.useRef(defaultModel);
53
61
  const sentInitialPromptId = react.useRef(null);
54
62
  react.useEffect(() => {
@@ -59,11 +67,15 @@ function Chat({
59
67
  react.useEffect(() => {
60
68
  if (modelsProp || !modelsApi) return;
61
69
  let cancelled = false;
62
- fetch(modelsApi).then((r) => r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))).then((data) => {
70
+ fetch(modelsApi).then(
71
+ (r) => r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))
72
+ ).then((data) => {
63
73
  if (cancelled) return;
64
74
  setModels(data);
65
75
  setModel((m) => m ?? controlledModel ?? models.defaultChatModelId(data));
66
- }).catch((err) => console.warn("clicky-ui: failed to load chat models", err));
76
+ }).catch(
77
+ (err) => console.warn("clicky-ui: failed to load chat models", err)
78
+ );
67
79
  return () => {
68
80
  cancelled = true;
69
81
  };
@@ -92,6 +104,7 @@ function Chat({
92
104
  bodyRef.current = {
93
105
  ...body,
94
106
  ...model ? { model } : {},
107
+ ...(selectedModel == null ? void 0 : selectedModel.runtime) ? { runtime: selectedModel.runtime } : {},
95
108
  ...effort ? { reasoningEffort: effort } : {},
96
109
  ...temperature !== void 0 ? { temperature } : {},
97
110
  ...budget && (budget.cost !== void 0 || budget.maxTokens !== void 0) ? { budget } : {},
@@ -102,7 +115,16 @@ function Chat({
102
115
  () => transport ?? new ai.DefaultChatTransport({ api, body: () => bodyRef.current }),
103
116
  [transport, api]
104
117
  );
105
- const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } = react$1.useChat({
118
+ const {
119
+ messages,
120
+ sendMessage,
121
+ regenerate,
122
+ addToolApprovalResponse,
123
+ status,
124
+ error,
125
+ clearError,
126
+ stop
127
+ } = react$1.useChat({
106
128
  transport: resolvedTransport,
107
129
  sendAutomaticallyWhen: ai.lastAssistantMessageIsCompleteWithApprovalResponses,
108
130
  ...initialMessages ? { messages: initialMessages } : {}
@@ -119,12 +141,12 @@ function Chat({
119
141
  const onUsageRef = react.useRef(onUsage);
120
142
  onUsageRef.current = onUsage;
121
143
  react.useEffect(() => {
122
- var _a, _b, _c;
144
+ var _a2, _b, _c;
123
145
  if (status !== "ready") return;
124
146
  const last = [...messages].reverse().find((m) => m.role === "assistant");
125
147
  const meta = last == null ? void 0 : last.metadata;
126
148
  if (!meta) return;
127
- const cost = meta.threadCostUsd ?? ((_a = meta.costBreakdown) == null ? void 0 : _a.totalUsd) ?? meta.cost;
149
+ const cost = meta.threadCostUsd ?? ((_a2 = meta.costBreakdown) == null ? void 0 : _a2.totalUsd) ?? meta.cost;
128
150
  const snapshot = {
129
151
  usedTokens: meta.contextTokens ?? ((_b = meta.usage) == null ? void 0 : _b.totalTokens) ?? 0,
130
152
  maxTokens: (selectedModel == null ? void 0 : selectedModel.contextWindow) ?? 0,
@@ -145,32 +167,76 @@ function Chat({
145
167
  setEffort(next);
146
168
  onReasoningEffortChange == null ? void 0 : onReasoningEffortChange(next);
147
169
  };
170
+ const resolveToolApproval = async (id, approved, reason) => {
171
+ setApprovalError(void 0);
172
+ if (approvalApi) {
173
+ try {
174
+ await approval.postToolApproval({
175
+ approvalApi,
176
+ threadId: threadId ?? "",
177
+ approvalId: id,
178
+ approved,
179
+ ...reason ? { reason } : {}
180
+ });
181
+ } catch (cause) {
182
+ setApprovalError(
183
+ cause instanceof Error ? cause : new Error(String(cause))
184
+ );
185
+ return;
186
+ }
187
+ }
188
+ await addToolApprovalResponse(
189
+ reason ? { id, approved, reason } : { id, approved }
190
+ );
191
+ };
148
192
  const ModelGlyph = providerIcons.providerIcon(selectedModel == null ? void 0 : selectedModel.provider);
149
- const toolbar = models$1.length > 0 || showEffort || usage && usage.maxTokens > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
193
+ const contextWindow = (selectedModel == null ? void 0 : selectedModel.contextWindow) ?? (usage == null ? void 0 : usage.maxTokens) ?? 0;
194
+ const usedTokens = (usage == null ? void 0 : usage.usedTokens) ?? 0;
195
+ const showContextMeter = Boolean(threadId || selectedModel || usage);
196
+ const toolbar = models$1.length > 0 || showEffort || showContextMeter ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
150
197
  /* @__PURE__ */ jsxRuntime.jsx(ModelSelector.ModelSelector, { models: models$1, value: model, onChange: onModelSelect }),
151
- showEffort && /* @__PURE__ */ jsxRuntime.jsx(ModelSelector.EffortSelector, { efforts: reasoningEfforts, value: effort, onChange: onEffortSelect }),
152
- usage && usage.maxTokens > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
198
+ showEffort && /* @__PURE__ */ jsxRuntime.jsx(
199
+ ModelSelector.EffortSelector,
200
+ {
201
+ efforts: reasoningEfforts,
202
+ value: effort,
203
+ onChange: onEffortSelect
204
+ }
205
+ ),
206
+ showContextMeter && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
153
207
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1" }),
154
208
  /* @__PURE__ */ jsxRuntime.jsx(
155
209
  ContextMeter.ContextMeter,
156
210
  {
157
211
  mode: "gauge",
158
- usedPercent: Math.round(usage.usedTokens / usage.maxTokens * 100),
159
- usedTokens: usage.usedTokens,
160
- windowTokens: usage.maxTokens,
161
- ...usage.messageCount != null ? { messageCount: usage.messageCount } : {},
162
- ...usage.cost != null ? { cost: { total: usage.cost } } : {},
163
- ...usage.modelLabel ? { model: usage.modelLabel } : {},
212
+ usedPercent: contextWindow > 0 ? Math.round(usedTokens / contextWindow * 100) : 0,
213
+ usedTokens,
214
+ ...contextWindow > 0 ? { windowTokens: contextWindow } : {},
215
+ ...(usage == null ? void 0 : usage.messageCount) != null ? { messageCount: usage.messageCount } : {},
216
+ ...(usage == null ? void 0 : usage.cost) != null ? { cost: { total: usage.cost } } : {},
217
+ ...threadId ? { sessionId: threadId } : {},
218
+ ...(selectedModel == null ? void 0 : selectedModel.label) ? { model: selectedModel.label } : (usage == null ? void 0 : usage.modelLabel) ? { model: usage.modelLabel } : {},
164
219
  ...effort ? { effort } : {},
220
+ ...((_a = selectedModel == null ? void 0 : selectedModel.runtime) == null ? void 0 : _a.mode) ? { executionMode: selectedModel.runtime.mode } : {},
165
221
  ...ModelGlyph ? { modelIcon: ModelGlyph } : {},
166
- ...(selectedModel == null ? void 0 : selectedModel.provider) ? { modelIconClassName: providerIcons.providerIconColor(selectedModel.provider) } : {}
222
+ ...(selectedModel == null ? void 0 : selectedModel.provider) ? {
223
+ modelIconClassName: providerIcons.providerIconColor(
224
+ selectedModel.provider
225
+ )
226
+ } : {}
167
227
  }
168
228
  )
169
229
  ] })
170
230
  ] }) : void 0;
171
231
  const empty = messages.length === 0 && (emptyState || (suggestions == null ? void 0 : suggestions.length)) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4", children: [
172
232
  emptyState,
173
- suggestions && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(Suggestion.Suggestions, { suggestions, onSelect: (text) => void sendMessage({ text }) })
233
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
234
+ Suggestion.Suggestions,
235
+ {
236
+ suggestions,
237
+ onSelect: (text) => void sendMessage({ text })
238
+ }
239
+ )
174
240
  ] }) : void 0;
175
241
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: utils.cn("flex h-full flex-col", className), children: [
176
242
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -178,13 +244,16 @@ function Chat({
178
244
  {
179
245
  messages,
180
246
  status,
181
- error,
182
- onClearError: clearError,
247
+ error: approvalError ?? error,
248
+ onClearError: () => {
249
+ setApprovalError(void 0);
250
+ clearError();
251
+ },
183
252
  ...threadId ? { sessionId: threadId } : {},
184
253
  ...model ? { model } : {},
185
254
  emptyState: empty,
186
255
  onRegenerate: (messageId) => void regenerate({ messageId }),
187
- onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved }),
256
+ onApprove: (id, approved, reason) => void resolveToolApproval(id, approved, reason),
188
257
  ...renderToolResult ? { renderToolResult } : {}
189
258
  }
190
259
  ),
@@ -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 {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon, providerIconColor } from \"./provider-icons\";\nimport { ContextMeter } from \"./ContextMeter\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolResultRenderer,\n} from \"./types\";\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 /** 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 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 /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\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 reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort = \"\",\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n renderToolResult,\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 [model, setModel] = useState<string | undefined>(controlledModel ?? defaultModel);\n const [effort, setEffort] = useState(controlledEffort ?? defaultReasoningEffort);\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n setModel((m) => m ?? controlledModel ?? defaultChatModelId(modelsProp));\n }, [modelsProp, controlledModel]);\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) => (r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))))\n .then((data: ChatModel[]) => {\n if (cancelled) return;\n setModels(data);\n setModel((m) => m ?? controlledModel ?? defaultChatModelId(data));\n })\n .catch((err) => console.warn(\"clicky-ui: failed to load chat models\", err));\n return () => {\n cancelled = true;\n };\n }, [modelsProp, modelsApi, controlledModel]);\n\n useEffect(() => {\n if (controlledModel === undefined) return;\n setModel(controlledModel);\n }, [controlledModel]);\n\n useEffect(() => {\n if (controlledEffort === undefined) return;\n setEffort(controlledEffort);\n }, [controlledEffort]);\n\n useEffect(() => {\n if (controlledModel !== undefined) return;\n if (!defaultModel || defaultModel === lastDefaultModel.current) return;\n lastDefaultModel.current = defaultModel;\n setModel(defaultModel);\n }, [controlledModel, defaultModel]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\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 ...(model ? { model } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\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 () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),\n [transport, api],\n );\n\n const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } =\n useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages ? { messages: initialMessages as ChatUIMessage[] } : {}),\n });\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") 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 }, [initialPrompt, onInitialPromptSent, sendMessage, status]);\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 cost = meta.threadCostUsd ?? meta.costBreakdown?.totalUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(meta.usage ? { usage: meta.usage } : {}),\n ...(meta.costBreakdown ? { costBreakdown: meta.costBreakdown } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const onEffortSelect = (next: string) => {\n setEffort(next);\n onReasoningEffortChange?.(next);\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const toolbar = models.length > 0 || showEffort || (usage && usage.maxTokens > 0) ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector efforts={reasoningEfforts} value={effort} onChange={onEffortSelect} />\n )}\n {usage && usage.maxTokens > 0 && (\n <>\n <div className=\"flex-1\" />\n <ContextMeter\n mode=\"gauge\"\n usedPercent={Math.round((usage.usedTokens / usage.maxTokens) * 100)}\n usedTokens={usage.usedTokens}\n windowTokens={usage.maxTokens}\n {...(usage.messageCount != null ? { messageCount: usage.messageCount } : {})}\n {...(usage.cost != null ? { cost: { total: usage.cost } } : {})}\n {...(usage.modelLabel ? { model: usage.modelLabel } : {})}\n {...(effort ? { effort } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n {...(selectedModel?.provider\n ? { modelIconClassName: providerIconColor(selectedModel.provider) }\n : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\n\n const empty = (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 suggestions={suggestions} onSelect={(text) => void sendMessage({ text })} />\n )}\n </div>\n ) : undefined;\n\n return (\n <div className={cn(\"flex h-full flex-col\", className)}>\n <Conversation\n messages={messages}\n status={status}\n error={error}\n onClearError={clearError}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(model ? { model } : {})}\n emptyState={empty}\n onRegenerate={(messageId) => void regenerate({ messageId })}\n onApprove={(id, approved, reason) =>\n void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved })\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 );\n}\n"],"names":["DEFAULT_REASONING_EFFORTS","attachmentUpload","models","useState","useRef","useEffect","defaultChatModelId","useMemo","createAttachmentUploadAdapter","DefaultChatTransport","useChat","lastAssistantMessageIsCompleteWithApprovalResponses","providerIcon","jsxs","jsx","ModelSelector","EffortSelector","Fragment","ContextMeter","providerIconColor","Suggestions","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 {\n DefaultChatTransport,\n lastAssistantMessageIsCompleteWithApprovalResponses,\n type ChatTransport,\n type UIMessage,\n} from \"ai\";\nimport { cn } from \"../../lib/utils\";\nimport { Conversation } from \"./Conversation\";\nimport { PromptInput } from \"./PromptInput\";\nimport { Suggestions } from \"./Suggestion\";\nimport { ModelSelector, EffortSelector } from \"./ModelSelector\";\nimport { providerIcon, providerIconColor } from \"./provider-icons\";\nimport { ContextMeter } from \"./ContextMeter\";\nimport { DEFAULT_REASONING_EFFORTS } from \"./effort-icons\";\nimport { defaultChatModelId } from \"./models\";\nimport { postToolApproval } from \"./approval\";\nimport {\n createAttachmentUploadAdapter,\n type AttachmentLimits,\n type AttachmentUploadAdapter,\n} from \"./attachment-upload\";\nimport type {\n ChatBudgetConfig,\n ChatModel,\n ChatMessageMetadata,\n ChatUsageSummary,\n ClaudePermissionMode,\n Suggestion,\n ToolResultRenderer,\n} from \"./types\";\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 /** 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 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 /** Thread id to persist this conversation under (forwarded in the body). */\n threadId?: string;\n /** Base endpoint for resolving a tool approval while the provider stream is\n * still active. The thread and approval ids are appended to this URL. */\n approvalApi?: string | null;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\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 reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n defaultReasoningEffort = \"\",\n reasoningEffort: controlledEffort,\n temperature,\n budget,\n permissionMode,\n onModelChange,\n onReasoningEffortChange,\n onUsage,\n suggestions,\n enableAttachments = false,\n attachmentsApi = \"/api/attachments\",\n attachmentUpload,\n attachmentLimits,\n threadId,\n approvalApi = null,\n renderToolResult,\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 [model, setModel] = useState<string | undefined>(\n controlledModel ?? defaultModel,\n );\n const [effort, setEffort] = useState(\n controlledEffort ?? defaultReasoningEffort,\n );\n const [usage, setUsage] = useState<ChatUsageSummary | null>(null);\n const [approvalError, setApprovalError] = useState<Error | undefined>();\n const lastDefaultModel = useRef(defaultModel);\n const sentInitialPromptId = useRef<number | null>(null);\n\n useEffect(() => {\n if (!modelsProp) return;\n setModels(modelsProp);\n setModel((m) => m ?? controlledModel ?? defaultChatModelId(modelsProp));\n }, [modelsProp, controlledModel]);\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 setModel((m) => m ?? controlledModel ?? defaultChatModelId(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, controlledModel]);\n\n useEffect(() => {\n if (controlledModel === undefined) return;\n setModel(controlledModel);\n }, [controlledModel]);\n\n useEffect(() => {\n if (controlledEffort === undefined) return;\n setEffort(controlledEffort);\n }, [controlledEffort]);\n\n useEffect(() => {\n if (controlledModel !== undefined) return;\n if (!defaultModel || defaultModel === lastDefaultModel.current) return;\n lastDefaultModel.current = defaultModel;\n setModel(defaultModel);\n }, [controlledModel, defaultModel]);\n\n const selectedModel = models.find((m) => m.id === model);\n const showEffort = !selectedModel || selectedModel.reasoning;\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 ...(model ? { model } : {}),\n ...(selectedModel?.runtime ? { runtime: selectedModel.runtime } : {}),\n ...(effort ? { reasoningEffort: effort } : {}),\n ...(temperature !== undefined ? { temperature } : {}),\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 sendMessage,\n regenerate,\n addToolApprovalResponse,\n status,\n error,\n clearError,\n stop,\n } = useChat<ChatUIMessage>({\n transport: resolvedTransport,\n sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,\n ...(initialMessages\n ? { messages: initialMessages as ChatUIMessage[] }\n : {}),\n });\n\n useEffect(() => {\n if (!initialPrompt || status !== \"ready\") 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 }, [initialPrompt, onInitialPromptSent, sendMessage, status]);\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 cost =\n meta.threadCostUsd ?? meta.costBreakdown?.totalUsd ?? meta.cost;\n const snapshot: ChatUsageSummary = {\n usedTokens: meta.contextTokens ?? meta.usage?.totalTokens ?? 0,\n maxTokens: selectedModel?.contextWindow ?? 0,\n messageCount: messages.length,\n ...(cost != null ? { cost } : {}),\n ...(meta.usage ? { usage: meta.usage } : {}),\n ...(meta.costBreakdown ? { costBreakdown: meta.costBreakdown } : {}),\n ...(selectedModel?.label ? { modelLabel: selectedModel.label } : {}),\n };\n setUsage(snapshot);\n onUsageRef.current?.(snapshot);\n }, [messages, status, selectedModel]);\n\n const onModelSelect = (id: string) => {\n setModel(id);\n onModelChange?.(id);\n };\n\n const onEffortSelect = (next: string) => {\n setEffort(next);\n onReasoningEffortChange?.(next);\n };\n\n const resolveToolApproval = async (\n id: string,\n approved: boolean,\n reason?: string,\n ) => {\n setApprovalError(undefined);\n if (approvalApi) {\n try {\n await postToolApproval({\n approvalApi,\n threadId: threadId ?? \"\",\n approvalId: id,\n approved,\n ...(reason ? { reason } : {}),\n });\n } catch (cause) {\n setApprovalError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n return;\n }\n }\n await addToolApprovalResponse(\n reason ? { id, approved, reason } : { id, approved },\n );\n };\n\n const ModelGlyph = providerIcon(selectedModel?.provider);\n const contextWindow = selectedModel?.contextWindow ?? usage?.maxTokens ?? 0;\n const usedTokens = usage?.usedTokens ?? 0;\n const showContextMeter = Boolean(threadId || selectedModel || usage);\n const toolbar =\n models.length > 0 || showEffort || showContextMeter ? (\n <div className=\"flex flex-1 items-center gap-2\">\n <ModelSelector models={models} value={model} onChange={onModelSelect} />\n {showEffort && (\n <EffortSelector\n efforts={reasoningEfforts}\n value={effort}\n onChange={onEffortSelect}\n />\n )}\n {showContextMeter && (\n <>\n <div className=\"flex-1\" />\n <ContextMeter\n mode=\"gauge\"\n usedPercent={\n contextWindow > 0\n ? Math.round((usedTokens / contextWindow) * 100)\n : 0\n }\n usedTokens={usedTokens}\n {...(contextWindow > 0 ? { windowTokens: contextWindow } : {})}\n {...(usage?.messageCount != null\n ? { messageCount: usage.messageCount }\n : {})}\n {...(usage?.cost != null ? { cost: { total: usage.cost } } : {})}\n {...(threadId ? { sessionId: threadId } : {})}\n {...(selectedModel?.label\n ? { model: selectedModel.label }\n : usage?.modelLabel\n ? { model: usage.modelLabel }\n : {})}\n {...(effort ? { effort } : {})}\n {...(selectedModel?.runtime?.mode\n ? { executionMode: selectedModel.runtime.mode }\n : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n {...(selectedModel?.provider\n ? {\n modelIconClassName: providerIconColor(\n selectedModel.provider,\n ),\n }\n : {})}\n />\n </>\n )}\n </div>\n ) : undefined;\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 <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 {...(model ? { model } : {})}\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 );\n}\n"],"names":["DEFAULT_REASONING_EFFORTS","attachmentUpload","models","useState","useRef","useEffect","defaultChatModelId","useMemo","createAttachmentUploadAdapter","DefaultChatTransport","useChat","lastAssistantMessageIsCompleteWithApprovalResponses","_a","postToolApproval","providerIcon","jsxs","jsx","ModelSelector","EffortSelector","Fragment","ContextMeter","providerIconColor","Suggestions","cn","Conversation","PromptInput"],"mappings":";;;;;;;;;;;;;;;;;AA2GO,SAAS,KAAK;AAAA,EACnB,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ;AAAA,EACA,OAAO;AAAA,EACP,mBAAmBA,YAAAA;AAAAA,EACnB,yBAAyB;AAAA,EACzB,iBAAiB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,oBAAoB;AAAA,EACpB,iBAAiB;AAAA,EAAA,kBACjBC;AAAAA,EACA;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAc;;AACZ,QAAM,CAACC,UAAQ,SAAS,IAAIC,MAAAA,SAAsB,cAAc,CAAA,CAAE;AAClE,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA;AAAAA,IACxB,mBAAmB;AAAA,EAAA;AAErB,QAAM,CAAC,QAAQ,SAAS,IAAIA,MAAAA;AAAAA,IAC1B,oBAAoB;AAAA,EAAA;AAEtB,QAAM,CAAC,OAAO,QAAQ,IAAIA,MAAAA,SAAkC,IAAI;AAChE,QAAM,CAAC,eAAe,gBAAgB,IAAIA,eAAA;AAC1C,QAAM,mBAAmBC,MAAAA,OAAO,YAAY;AAC5C,QAAM,sBAAsBA,MAAAA,OAAsB,IAAI;AAEtDC,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,WAAY;AACjB,cAAU,UAAU;AACpB,aAAS,CAAC,MAAM,KAAK,mBAAmBC,OAAAA,mBAAmB,UAAU,CAAC;AAAA,EACxE,GAAG,CAAC,YAAY,eAAe,CAAC;AAGhCD,QAAAA,UAAU,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;AACd,eAAS,CAAC,MAAM,KAAK,mBAAmBC,OAAAA,mBAAmB,IAAI,CAAC;AAAA,IAClE,CAAC,EACA;AAAA,MAAM,CAAC,QACN,QAAQ,KAAK,yCAAyC,GAAG;AAAA,IAAA;AAE7D,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,eAAe,CAAC;AAE3CD,QAAAA,UAAU,MAAM;AACd,QAAI,oBAAoB,OAAW;AACnC,aAAS,eAAe;AAAA,EAC1B,GAAG,CAAC,eAAe,CAAC;AAEpBA,QAAAA,UAAU,MAAM;AACd,QAAI,qBAAqB,OAAW;AACpC,cAAU,gBAAgB;AAAA,EAC5B,GAAG,CAAC,gBAAgB,CAAC;AAErBA,QAAAA,UAAU,MAAM;AACd,QAAI,oBAAoB,OAAW;AACnC,QAAI,CAAC,gBAAgB,iBAAiB,iBAAiB,QAAS;AAChE,qBAAiB,UAAU;AAC3B,aAAS,YAAY;AAAA,EACvB,GAAG,CAAC,iBAAiB,YAAY,CAAC;AAElC,QAAM,gBAAgBH,SAAO,KAAK,CAAC,MAAM,EAAE,OAAO,KAAK;AACvD,QAAM,aAAa,CAAC,iBAAiB,cAAc;AACnD,QAAM,2BAA2BK,MAAAA;AAAAA,IAC/B,MACEN,sBACAO,iBAAAA,8BAA8B,EAAE,UAAU,gBAAgB;AAAA,IAC5D,CAACP,oBAAkB,cAAc;AAAA,EAAA;AAKnC,QAAM,UAAUG,MAAAA,OAAgC,EAAE;AAClD,UAAQ,UAAU;AAAA,IAChB,GAAG;AAAA,IACH,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA;AAAA,IACxB,IAAI,+CAAe,WAAU,EAAE,SAAS,cAAc,QAAA,IAAY,CAAA;AAAA,IAClE,GAAI,SAAS,EAAE,iBAAiB,OAAA,IAAW,CAAA;AAAA,IAC3C,GAAI,gBAAgB,SAAY,EAAE,YAAA,IAAgB,CAAA;AAAA,IAClD,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,oBAAoBG,MAAAA;AAAAA,IACxB,MACE,aACA,IAAIE,GAAAA,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,IACEC,gBAAuB;AAAA,IACzB,WAAW;AAAA,IACX,uBAAuBC,GAAAA;AAAAA,IACvB,GAAI,kBACA,EAAE,UAAU,oBACZ,CAAA;AAAA,EAAC,CACN;AAEDN,QAAAA,UAAU,MAAM;AACd,QAAI,CAAC,iBAAiB,WAAW,QAAS;AAC1C,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,CAAC,eAAe,qBAAqB,aAAa,MAAM,CAAC;AAK5D,QAAM,aAAaD,MAAAA,OAAO,OAAO;AACjC,aAAW,UAAU;AACrBC,QAAAA,UAAU,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,KAAM;AACX,UAAM,OACJ,KAAK,mBAAiBO,MAAA,KAAK,kBAAL,gBAAAA,IAAoB,aAAY,KAAK;AAC7D,UAAM,WAA6B;AAAA,MACjC,YAAY,KAAK,mBAAiB,UAAK,UAAL,mBAAY,gBAAe;AAAA,MAC7D,YAAW,+CAAe,kBAAiB;AAAA,MAC3C,cAAc,SAAS;AAAA,MACvB,GAAI,QAAQ,OAAO,EAAE,KAAA,IAAS,CAAA;AAAA,MAC9B,GAAI,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAA,IAAU,CAAA;AAAA,MACzC,GAAI,KAAK,gBAAgB,EAAE,eAAe,KAAK,cAAA,IAAkB,CAAA;AAAA,MACjE,IAAI,+CAAe,SAAQ,EAAE,YAAY,cAAc,MAAA,IAAU,CAAA;AAAA,IAAC;AAEpE,aAAS,QAAQ;AACjB,qBAAW,YAAX,oCAAqB;AAAA,EACvB,GAAG,CAAC,UAAU,QAAQ,aAAa,CAAC;AAEpC,QAAM,gBAAgB,CAAC,OAAe;AACpC,aAAS,EAAE;AACX,mDAAgB;AAAA,EAClB;AAEA,QAAM,iBAAiB,CAAC,SAAiB;AACvC,cAAU,IAAI;AACd,uEAA0B;AAAA,EAC5B;AAEA,QAAM,sBAAsB,OAC1B,IACA,UACA,WACG;AACH,qBAAiB,MAAS;AAC1B,QAAI,aAAa;AACf,UAAI;AACF,cAAMC,0BAAiB;AAAA,UACrB;AAAA,UACA,UAAU,YAAY;AAAA,UACtB,YAAY;AAAA,UACZ;AAAA,UACA,GAAI,SAAS,EAAE,WAAW,CAAA;AAAA,QAAC,CAC5B;AAAA,MACH,SAAS,OAAO;AACd;AAAA,UACE,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,QAAA;AAE1D;AAAA,MACF;AAAA,IACF;AACA,UAAM;AAAA,MACJ,SAAS,EAAE,IAAI,UAAU,WAAW,EAAE,IAAI,SAAA;AAAA,IAAS;AAAA,EAEvD;AAEA,QAAM,aAAaC,cAAAA,aAAa,+CAAe,QAAQ;AACvD,QAAM,iBAAgB,+CAAe,mBAAiB,+BAAO,cAAa;AAC1E,QAAM,cAAa,+BAAO,eAAc;AACxC,QAAM,mBAAmB,QAAQ,YAAY,iBAAiB,KAAK;AACnE,QAAM,UACJZ,SAAO,SAAS,KAAK,cAAc,mBACjCa,2BAAAA,KAAC,OAAA,EAAI,WAAU,kCACb,UAAA;AAAA,IAAAC,2BAAAA,IAACC,cAAAA,eAAA,UAAcf,UAAgB,OAAO,OAAO,UAAU,eAAe;AAAA,IACrE,cACCc,2BAAAA;AAAAA,MAACE,cAAAA;AAAAA,MAAA;AAAA,QACC,SAAS;AAAA,QACT,OAAO;AAAA,QACP,UAAU;AAAA,MAAA;AAAA,IAAA;AAAA,IAGb,oBACCH,2BAAAA,KAAAI,qBAAA,EACE,UAAA;AAAA,MAAAH,2BAAAA,IAAC,OAAA,EAAI,WAAU,SAAA,CAAS;AAAA,MACxBA,2BAAAA;AAAAA,QAACI,aAAAA;AAAAA,QAAA;AAAA,UACC,MAAK;AAAA,UACL,aACE,gBAAgB,IACZ,KAAK,MAAO,aAAa,gBAAiB,GAAG,IAC7C;AAAA,UAEN;AAAA,UACC,GAAI,gBAAgB,IAAI,EAAE,cAAc,cAAA,IAAkB,CAAA;AAAA,UAC1D,IAAI,+BAAO,iBAAgB,OACxB,EAAE,cAAc,MAAM,aAAA,IACtB,CAAA;AAAA,UACH,IAAI,+BAAO,SAAQ,OAAO,EAAE,MAAM,EAAE,OAAO,MAAM,KAAA,EAAK,IAAM,CAAA;AAAA,UAC5D,GAAI,WAAW,EAAE,WAAW,SAAA,IAAa,CAAA;AAAA,UACzC,IAAI,+CAAe,SAChB,EAAE,OAAO,cAAc,MAAA,KACvB,+BAAO,cACL,EAAE,OAAO,MAAM,WAAA,IACf,CAAA;AAAA,UACL,GAAI,SAAS,EAAE,OAAA,IAAW,CAAA;AAAA,UAC1B,KAAI,oDAAe,YAAf,mBAAwB,QACzB,EAAE,eAAe,cAAc,QAAQ,KAAA,IACvC,CAAA;AAAA,UACH,GAAI,aAAa,EAAE,WAAW,WAAA,IAAe,CAAA;AAAA,UAC7C,IAAI,+CAAe,YAChB;AAAA,YACE,oBAAoBC,cAAAA;AAAAA,cAClB,cAAc;AAAA,YAAA;AAAA,UAChB,IAEF,CAAA;AAAA,QAAC;AAAA,MAAA;AAAA,IACP,EAAA,CACF;AAAA,EAAA,EAAA,CAEJ,IACE;AAEN,QAAM,QACJ,SAAS,WAAW,MAAM,eAAc,2CAAa,WACnDN,2BAAAA,KAAC,OAAA,EAAI,WAAU,oCACZ,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,SAAS,KACnCC,2BAAAA;AAAAA,MAACM,WAAAA;AAAAA,MAAA;AAAA,QACC;AAAA,QACA,UAAU,CAAC,SAAS,KAAK,YAAY,EAAE,MAAM;AAAA,MAAA;AAAA,IAAA;AAAA,EAC/C,EAAA,CAEJ,IACE;AAEN,yCACG,OAAA,EAAI,WAAWC,MAAAA,GAAG,wBAAwB,SAAS,GAClD,UAAA;AAAA,IAAAP,2BAAAA;AAAAA,MAACQ,aAAAA;AAAAA,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,GAAI,QAAQ,EAAE,MAAA,IAAU,CAAA;AAAA,QACzB,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,IAElDR,2BAAAA,IAAC,OAAA,EAAI,WAAU,YACb,UAAAA,2BAAAA;AAAAA,MAACS,YAAAA;AAAAA,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,GACF;AAEJ;;"}
@@ -46,6 +46,9 @@ export type ChatProps = {
46
46
  attachmentLimits?: AttachmentLimits;
47
47
  /** Thread id to persist this conversation under (forwarded in the body). */
48
48
  threadId?: string;
49
+ /** Base endpoint for resolving a tool approval while the provider stream is
50
+ * still active. The thread and approval ids are appended to this URL. */
51
+ approvalApi?: string | null;
49
52
  /** Optional host renderer for recognized completed tool outputs. */
50
53
  renderToolResult?: ToolResultRenderer;
51
54
  /** Extra fields merged into every request body. */
@@ -70,5 +73,5 @@ export type ChatProps = {
70
73
  * per-message copy/regenerate, and human-in-the-loop tool approvals. The
71
74
  * backend owns model selection and tool execution; the selected model,
72
75
  * effort, permission mode and thread id are forwarded in the request body. */
73
- export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, model: controlledModel, reasoningEfforts, defaultReasoningEffort, reasoningEffort: controlledEffort, temperature, budget, permissionMode, onModelChange, onReasoningEffortChange, onUsage, suggestions, enableAttachments, attachmentsApi, attachmentUpload, attachmentLimits, threadId, renderToolResult, body, transport, initialMessages, initialPrompt, onInitialPromptSent, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
76
+ export declare function Chat({ api, models: modelsProp, modelsApi, defaultModel, model: controlledModel, reasoningEfforts, defaultReasoningEffort, reasoningEffort: controlledEffort, temperature, budget, permissionMode, onModelChange, onReasoningEffortChange, onUsage, suggestions, enableAttachments, attachmentsApi, attachmentUpload, attachmentLimits, threadId, approvalApi, renderToolResult, body, transport, initialMessages, initialPrompt, onInitialPromptSent, placeholder, emptyState, className, }: ChatProps): import("react/jsx-runtime").JSX.Element;
74
77
  //# 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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"}
@@ -11,6 +11,7 @@ import { providerIcon, providerIconColor } from "./provider-icons.js";
11
11
  import { ContextMeter } from "./ContextMeter.js";
12
12
  import { DEFAULT_REASONING_EFFORTS } from "./effort-icons.js";
13
13
  import { defaultChatModelId } from "./models.js";
14
+ import { postToolApproval } from "./approval.js";
14
15
  import { createAttachmentUploadAdapter } from "./attachment-upload.js";
15
16
  function Chat({
16
17
  api = "/api/chat",
@@ -33,6 +34,7 @@ function Chat({
33
34
  attachmentUpload,
34
35
  attachmentLimits,
35
36
  threadId,
37
+ approvalApi = null,
36
38
  renderToolResult,
37
39
  body,
38
40
  transport,
@@ -43,10 +45,16 @@ function Chat({
43
45
  emptyState,
44
46
  className
45
47
  }) {
48
+ var _a;
46
49
  const [models, setModels] = useState(modelsProp ?? []);
47
- const [model, setModel] = useState(controlledModel ?? defaultModel);
48
- const [effort, setEffort] = useState(controlledEffort ?? defaultReasoningEffort);
50
+ const [model, setModel] = useState(
51
+ controlledModel ?? defaultModel
52
+ );
53
+ const [effort, setEffort] = useState(
54
+ controlledEffort ?? defaultReasoningEffort
55
+ );
49
56
  const [usage, setUsage] = useState(null);
57
+ const [approvalError, setApprovalError] = useState();
50
58
  const lastDefaultModel = useRef(defaultModel);
51
59
  const sentInitialPromptId = useRef(null);
52
60
  useEffect(() => {
@@ -57,11 +65,15 @@ function Chat({
57
65
  useEffect(() => {
58
66
  if (modelsProp || !modelsApi) return;
59
67
  let cancelled = false;
60
- fetch(modelsApi).then((r) => r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))).then((data) => {
68
+ fetch(modelsApi).then(
69
+ (r) => r.ok ? r.json() : Promise.reject(new Error(`models ${r.status}`))
70
+ ).then((data) => {
61
71
  if (cancelled) return;
62
72
  setModels(data);
63
73
  setModel((m) => m ?? controlledModel ?? defaultChatModelId(data));
64
- }).catch((err) => console.warn("clicky-ui: failed to load chat models", err));
74
+ }).catch(
75
+ (err) => console.warn("clicky-ui: failed to load chat models", err)
76
+ );
65
77
  return () => {
66
78
  cancelled = true;
67
79
  };
@@ -90,6 +102,7 @@ function Chat({
90
102
  bodyRef.current = {
91
103
  ...body,
92
104
  ...model ? { model } : {},
105
+ ...(selectedModel == null ? void 0 : selectedModel.runtime) ? { runtime: selectedModel.runtime } : {},
93
106
  ...effort ? { reasoningEffort: effort } : {},
94
107
  ...temperature !== void 0 ? { temperature } : {},
95
108
  ...budget && (budget.cost !== void 0 || budget.maxTokens !== void 0) ? { budget } : {},
@@ -100,7 +113,16 @@ function Chat({
100
113
  () => transport ?? new DefaultChatTransport({ api, body: () => bodyRef.current }),
101
114
  [transport, api]
102
115
  );
103
- const { messages, sendMessage, regenerate, addToolApprovalResponse, status, error, clearError, stop } = useChat({
116
+ const {
117
+ messages,
118
+ sendMessage,
119
+ regenerate,
120
+ addToolApprovalResponse,
121
+ status,
122
+ error,
123
+ clearError,
124
+ stop
125
+ } = useChat({
104
126
  transport: resolvedTransport,
105
127
  sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
106
128
  ...initialMessages ? { messages: initialMessages } : {}
@@ -117,12 +139,12 @@ function Chat({
117
139
  const onUsageRef = useRef(onUsage);
118
140
  onUsageRef.current = onUsage;
119
141
  useEffect(() => {
120
- var _a, _b, _c;
142
+ var _a2, _b, _c;
121
143
  if (status !== "ready") return;
122
144
  const last = [...messages].reverse().find((m) => m.role === "assistant");
123
145
  const meta = last == null ? void 0 : last.metadata;
124
146
  if (!meta) return;
125
- const cost = meta.threadCostUsd ?? ((_a = meta.costBreakdown) == null ? void 0 : _a.totalUsd) ?? meta.cost;
147
+ const cost = meta.threadCostUsd ?? ((_a2 = meta.costBreakdown) == null ? void 0 : _a2.totalUsd) ?? meta.cost;
126
148
  const snapshot = {
127
149
  usedTokens: meta.contextTokens ?? ((_b = meta.usage) == null ? void 0 : _b.totalTokens) ?? 0,
128
150
  maxTokens: (selectedModel == null ? void 0 : selectedModel.contextWindow) ?? 0,
@@ -143,32 +165,76 @@ function Chat({
143
165
  setEffort(next);
144
166
  onReasoningEffortChange == null ? void 0 : onReasoningEffortChange(next);
145
167
  };
168
+ const resolveToolApproval = async (id, approved, reason) => {
169
+ setApprovalError(void 0);
170
+ if (approvalApi) {
171
+ try {
172
+ await postToolApproval({
173
+ approvalApi,
174
+ threadId: threadId ?? "",
175
+ approvalId: id,
176
+ approved,
177
+ ...reason ? { reason } : {}
178
+ });
179
+ } catch (cause) {
180
+ setApprovalError(
181
+ cause instanceof Error ? cause : new Error(String(cause))
182
+ );
183
+ return;
184
+ }
185
+ }
186
+ await addToolApprovalResponse(
187
+ reason ? { id, approved, reason } : { id, approved }
188
+ );
189
+ };
146
190
  const ModelGlyph = providerIcon(selectedModel == null ? void 0 : selectedModel.provider);
147
- const toolbar = models.length > 0 || showEffort || usage && usage.maxTokens > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
191
+ const contextWindow = (selectedModel == null ? void 0 : selectedModel.contextWindow) ?? (usage == null ? void 0 : usage.maxTokens) ?? 0;
192
+ const usedTokens = (usage == null ? void 0 : usage.usedTokens) ?? 0;
193
+ const showContextMeter = Boolean(threadId || selectedModel || usage);
194
+ const toolbar = models.length > 0 || showEffort || showContextMeter ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 items-center gap-2", children: [
148
195
  /* @__PURE__ */ jsx(ModelSelector, { models, value: model, onChange: onModelSelect }),
149
- showEffort && /* @__PURE__ */ jsx(EffortSelector, { efforts: reasoningEfforts, value: effort, onChange: onEffortSelect }),
150
- usage && usage.maxTokens > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
196
+ showEffort && /* @__PURE__ */ jsx(
197
+ EffortSelector,
198
+ {
199
+ efforts: reasoningEfforts,
200
+ value: effort,
201
+ onChange: onEffortSelect
202
+ }
203
+ ),
204
+ showContextMeter && /* @__PURE__ */ jsxs(Fragment, { children: [
151
205
  /* @__PURE__ */ jsx("div", { className: "flex-1" }),
152
206
  /* @__PURE__ */ jsx(
153
207
  ContextMeter,
154
208
  {
155
209
  mode: "gauge",
156
- usedPercent: Math.round(usage.usedTokens / usage.maxTokens * 100),
157
- usedTokens: usage.usedTokens,
158
- windowTokens: usage.maxTokens,
159
- ...usage.messageCount != null ? { messageCount: usage.messageCount } : {},
160
- ...usage.cost != null ? { cost: { total: usage.cost } } : {},
161
- ...usage.modelLabel ? { model: usage.modelLabel } : {},
210
+ usedPercent: contextWindow > 0 ? Math.round(usedTokens / contextWindow * 100) : 0,
211
+ usedTokens,
212
+ ...contextWindow > 0 ? { windowTokens: contextWindow } : {},
213
+ ...(usage == null ? void 0 : usage.messageCount) != null ? { messageCount: usage.messageCount } : {},
214
+ ...(usage == null ? void 0 : usage.cost) != null ? { cost: { total: usage.cost } } : {},
215
+ ...threadId ? { sessionId: threadId } : {},
216
+ ...(selectedModel == null ? void 0 : selectedModel.label) ? { model: selectedModel.label } : (usage == null ? void 0 : usage.modelLabel) ? { model: usage.modelLabel } : {},
162
217
  ...effort ? { effort } : {},
218
+ ...((_a = selectedModel == null ? void 0 : selectedModel.runtime) == null ? void 0 : _a.mode) ? { executionMode: selectedModel.runtime.mode } : {},
163
219
  ...ModelGlyph ? { modelIcon: ModelGlyph } : {},
164
- ...(selectedModel == null ? void 0 : selectedModel.provider) ? { modelIconClassName: providerIconColor(selectedModel.provider) } : {}
220
+ ...(selectedModel == null ? void 0 : selectedModel.provider) ? {
221
+ modelIconClassName: providerIconColor(
222
+ selectedModel.provider
223
+ )
224
+ } : {}
165
225
  }
166
226
  )
167
227
  ] })
168
228
  ] }) : void 0;
169
229
  const empty = messages.length === 0 && (emptyState || (suggestions == null ? void 0 : suggestions.length)) ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-4", children: [
170
230
  emptyState,
171
- suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx(Suggestions, { suggestions, onSelect: (text) => void sendMessage({ text }) })
231
+ suggestions && suggestions.length > 0 && /* @__PURE__ */ jsx(
232
+ Suggestions,
233
+ {
234
+ suggestions,
235
+ onSelect: (text) => void sendMessage({ text })
236
+ }
237
+ )
172
238
  ] }) : void 0;
173
239
  return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full flex-col", className), children: [
174
240
  /* @__PURE__ */ jsx(
@@ -176,13 +242,16 @@ function Chat({
176
242
  {
177
243
  messages,
178
244
  status,
179
- error,
180
- onClearError: clearError,
245
+ error: approvalError ?? error,
246
+ onClearError: () => {
247
+ setApprovalError(void 0);
248
+ clearError();
249
+ },
181
250
  ...threadId ? { sessionId: threadId } : {},
182
251
  ...model ? { model } : {},
183
252
  emptyState: empty,
184
253
  onRegenerate: (messageId) => void regenerate({ messageId }),
185
- onApprove: (id, approved, reason) => void addToolApprovalResponse(reason ? { id, approved, reason } : { id, approved }),
254
+ onApprove: (id, approved, reason) => void resolveToolApproval(id, approved, reason),
186
255
  ...renderToolResult ? { renderToolResult } : {}
187
256
  }
188
257
  ),