@flanksource/clicky-ui 0.3.23 → 0.3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/data/ai/AdvancedChatConfig.cjs +3 -0
- package/dist/data/ai/AdvancedChatConfig.cjs.map +1 -1
- package/dist/data/ai/AdvancedChatConfig.d.ts +2 -1
- package/dist/data/ai/AdvancedChatConfig.d.ts.map +1 -1
- package/dist/data/ai/AdvancedChatConfig.js +3 -0
- package/dist/data/ai/AdvancedChatConfig.js.map +1 -1
- package/dist/data/ai/ChatWindow.cjs +77 -4
- package/dist/data/ai/ChatWindow.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.js +78 -5
- package/dist/data/ai/ChatWindow.js.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.cjs +19 -4
- package/dist/data/ai/ChatWindow.runtime.cjs.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.d.ts +2 -0
- package/dist/data/ai/ChatWindow.runtime.d.ts.map +1 -1
- package/dist/data/ai/ChatWindow.runtime.js +19 -4
- package/dist/data/ai/ChatWindow.runtime.js.map +1 -1
- package/dist/data/ai/ChatWindow.test-utils.d.ts +2 -1
- package/dist/data/ai/ChatWindow.test-utils.d.ts.map +1 -1
- package/dist/data/ai/ToolPreferences.cjs +8 -1
- package/dist/data/ai/ToolPreferences.cjs.map +1 -1
- package/dist/data/ai/ToolPreferences.d.ts +2 -1
- package/dist/data/ai/ToolPreferences.d.ts.map +1 -1
- package/dist/data/ai/ToolPreferences.js +8 -1
- package/dist/data/ai/ToolPreferences.js.map +1 -1
- package/dist/data/chat/Chat.cjs +26 -3
- package/dist/data/chat/Chat.cjs.map +1 -1
- package/dist/data/chat/Chat.d.ts +9 -1
- package/dist/data/chat/Chat.d.ts.map +1 -1
- package/dist/data/chat/Chat.js +26 -3
- package/dist/data/chat/Chat.js.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.cjs +2 -0
- package/dist/data/chat/ChatRuntimeToolbar.cjs.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.d.ts +2 -1
- package/dist/data/chat/ChatRuntimeToolbar.d.ts.map +1 -1
- package/dist/data/chat/ChatRuntimeToolbar.js +2 -0
- package/dist/data/chat/ChatRuntimeToolbar.js.map +1 -1
- package/dist/data/chat/Message.cjs +36 -1
- package/dist/data/chat/Message.cjs.map +1 -1
- package/dist/data/chat/Message.d.ts +1 -1
- package/dist/data/chat/Message.d.ts.map +1 -1
- package/dist/data/chat/Message.js +37 -2
- package/dist/data/chat/Message.js.map +1 -1
- package/dist/data/chat/approval.cjs +17 -0
- package/dist/data/chat/approval.cjs.map +1 -1
- package/dist/data/chat/approval.d.ts +5 -0
- package/dist/data/chat/approval.d.ts.map +1 -1
- package/dist/data/chat/approval.js +17 -0
- package/dist/data/chat/approval.js.map +1 -1
- package/dist/data/chat/fork-seed.cjs +22 -0
- package/dist/data/chat/fork-seed.cjs.map +1 -0
- package/dist/data/chat/fork-seed.d.ts +8 -0
- package/dist/data/chat/fork-seed.d.ts.map +1 -0
- package/dist/data/chat/fork-seed.js +22 -0
- package/dist/data/chat/fork-seed.js.map +1 -0
- package/dist/data/runtime/RuntimeBar.cjs +3 -0
- package/dist/data/runtime/RuntimeBar.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBar.d.ts +3 -1
- package/dist/data/runtime/RuntimeBar.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBar.js +3 -0
- package/dist/data/runtime/RuntimeBar.js.map +1 -1
- package/dist/data/runtime/RuntimeBarCombo.cjs +15 -1
- package/dist/data/runtime/RuntimeBarCombo.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarCombo.d.ts +2 -1
- package/dist/data/runtime/RuntimeBarCombo.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarCombo.js +15 -1
- package/dist/data/runtime/RuntimeBarCombo.js.map +1 -1
- package/dist/data/runtime/RuntimeBarSegments.cjs +7 -0
- package/dist/data/runtime/RuntimeBarSegments.cjs.map +1 -1
- package/dist/data/runtime/RuntimeBarSegments.d.ts +2 -1
- package/dist/data/runtime/RuntimeBarSegments.d.ts.map +1 -1
- package/dist/data/runtime/RuntimeBarSegments.js +7 -0
- package/dist/data/runtime/RuntimeBarSegments.js.map +1 -1
- package/dist/data/version-info.cjs +3 -3
- package/dist/data/version-info.js +3 -3
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatRuntimeToolbar.d.ts","sourceRoot":"","sources":["../../../src/data/chat/ChatRuntimeToolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,CAAC,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC;IAClD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACtC,KAAK,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,QAAQ,EACR,eAAe,GAChB,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"ChatRuntimeToolbar.d.ts","sourceRoot":"","sources":["../../../src/data/chat/ChatRuntimeToolbar.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE7E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAEjE,MAAM,MAAM,uBAAuB,GAAG;IACpC,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,eAAe,CAAC,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC;IAClD,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACtC,KAAK,CAAC,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,OAAO,EACP,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,KAAK,EACL,QAAQ,EACR,MAAc,EACd,eAAe,GAChB,EAAE,uBAAuB,kDA+DzB"}
|
|
@@ -10,6 +10,7 @@ function ChatRuntimeToolbar({
|
|
|
10
10
|
selectedModel,
|
|
11
11
|
usage,
|
|
12
12
|
threadId,
|
|
13
|
+
locked = false,
|
|
13
14
|
onRuntimeChange
|
|
14
15
|
}) {
|
|
15
16
|
const showRuntimeBar = Boolean(
|
|
@@ -29,6 +30,7 @@ function ChatRuntimeToolbar({
|
|
|
29
30
|
onChange: onRuntimeChange,
|
|
30
31
|
models,
|
|
31
32
|
reasoningEfforts,
|
|
33
|
+
locked,
|
|
32
34
|
...runtimeFamilies ? { families: runtimeFamilies } : {}
|
|
33
35
|
}
|
|
34
36
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatRuntimeToolbar.js","sources":["../../../src/data/chat/ChatRuntimeToolbar.tsx"],"sourcesContent":["import { ContextMeter } from \"./ContextMeter\";\nimport { providerIcon, providerIconColor } from \"./provider-icons\";\nimport type { ChatModel, ChatModelRuntime, ChatUsageSummary } from \"./types\";\nimport { RuntimeBar } from \"../runtime/RuntimeBar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\n\nexport type ChatRuntimeToolbarProps = {\n models: ChatModel[];\n runtime: ChatModelRuntime;\n runtimeFamilies?: SpecRuntimeFamily[] | undefined;\n reasoningEfforts: string[];\n selectedModel?: ChatModel | undefined;\n usage?: ChatUsageSummary | null | undefined;\n threadId?: string | undefined;\n onRuntimeChange: (runtime: ChatModelRuntime) => void;\n};\n\nexport function ChatRuntimeToolbar({\n models,\n runtime,\n runtimeFamilies,\n reasoningEfforts,\n selectedModel,\n usage,\n threadId,\n onRuntimeChange,\n}: ChatRuntimeToolbarProps) {\n const showRuntimeBar = Boolean(\n runtimeFamilies?.length ||\n models.length ||\n runtime.backend ||\n runtime.id ||\n runtime.model,\n );\n const showContextMeter = Boolean(threadId || selectedModel || usage);\n if (!showRuntimeBar && !showContextMeter) return null;\n\n const contextWindow = selectedModel?.contextWindow ?? usage?.maxTokens ?? 0;\n const usedTokens = usage?.usedTokens ?? 0;\n const ModelGlyph = providerIcon(selectedModel?.provider);\n\n return (\n <div className=\"flex flex-1 items-center gap-2\">\n {showRuntimeBar && (\n <RuntimeBar\n variant=\"combo\"\n value={runtime}\n onChange={onRuntimeChange}\n models={models}\n reasoningEfforts={reasoningEfforts}\n {...(runtimeFamilies ? { families: runtimeFamilies } : {})}\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 {...(runtime.effort ? { effort: runtime.effort } : {})}\n {...(runtime.mode ? { executionMode: runtime.mode } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n {...(selectedModel?.provider\n ? {\n modelIconClassName: providerIconColor(selectedModel.provider),\n }\n : {})}\n />\n </>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"ChatRuntimeToolbar.js","sources":["../../../src/data/chat/ChatRuntimeToolbar.tsx"],"sourcesContent":["import { ContextMeter } from \"./ContextMeter\";\nimport { providerIcon, providerIconColor } from \"./provider-icons\";\nimport type { ChatModel, ChatModelRuntime, ChatUsageSummary } from \"./types\";\nimport { RuntimeBar } from \"../runtime/RuntimeBar\";\nimport type { SpecRuntimeFamily } from \"../runtime/runtime-mode\";\n\nexport type ChatRuntimeToolbarProps = {\n models: ChatModel[];\n runtime: ChatModelRuntime;\n runtimeFamilies?: SpecRuntimeFamily[] | undefined;\n reasoningEfforts: string[];\n selectedModel?: ChatModel | undefined;\n usage?: ChatUsageSummary | null | undefined;\n threadId?: string | undefined;\n locked?: boolean | undefined;\n onRuntimeChange: (runtime: ChatModelRuntime) => void;\n};\n\nexport function ChatRuntimeToolbar({\n models,\n runtime,\n runtimeFamilies,\n reasoningEfforts,\n selectedModel,\n usage,\n threadId,\n locked = false,\n onRuntimeChange,\n}: ChatRuntimeToolbarProps) {\n const showRuntimeBar = Boolean(\n runtimeFamilies?.length ||\n models.length ||\n runtime.backend ||\n runtime.id ||\n runtime.model,\n );\n const showContextMeter = Boolean(threadId || selectedModel || usage);\n if (!showRuntimeBar && !showContextMeter) return null;\n\n const contextWindow = selectedModel?.contextWindow ?? usage?.maxTokens ?? 0;\n const usedTokens = usage?.usedTokens ?? 0;\n const ModelGlyph = providerIcon(selectedModel?.provider);\n\n return (\n <div className=\"flex flex-1 items-center gap-2\">\n {showRuntimeBar && (\n <RuntimeBar\n variant=\"combo\"\n value={runtime}\n onChange={onRuntimeChange}\n models={models}\n reasoningEfforts={reasoningEfforts}\n locked={locked}\n {...(runtimeFamilies ? { families: runtimeFamilies } : {})}\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 {...(runtime.effort ? { effort: runtime.effort } : {})}\n {...(runtime.mode ? { executionMode: runtime.mode } : {})}\n {...(ModelGlyph ? { modelIcon: ModelGlyph } : {})}\n {...(selectedModel?.provider\n ? {\n modelIconClassName: providerIconColor(selectedModel.provider),\n }\n : {})}\n />\n </>\n )}\n </div>\n );\n}\n"],"names":[],"mappings":";;;;AAkBO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT;AACF,GAA4B;AAC1B,QAAM,iBAAiB;AAAA,KACrB,mDAAiB,WACjB,OAAO,UACP,QAAQ,WACR,QAAQ,MACR,QAAQ;AAAA,EAAA;AAEV,QAAM,mBAAmB,QAAQ,YAAY,iBAAiB,KAAK;AACnE,MAAI,CAAC,kBAAkB,CAAC,iBAAkB,QAAO;AAEjD,QAAM,iBAAgB,+CAAe,mBAAiB,+BAAO,cAAa;AAC1E,QAAM,cAAa,+BAAO,eAAc;AACxC,QAAM,aAAa,aAAa,+CAAe,QAAQ;AAEvD,SACE,qBAAC,OAAA,EAAI,WAAU,kCACZ,UAAA;AAAA,IAAA,kBACC;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,SAAQ;AAAA,QACR,OAAO;AAAA,QACP,UAAU;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,QACC,GAAI,kBAAkB,EAAE,UAAU,oBAAoB,CAAA;AAAA,MAAC;AAAA,IAAA;AAAA,IAG3D,oBACC,qBAAA,UAAA,EACE,UAAA;AAAA,MAAA,oBAAC,OAAA,EAAI,WAAU,SAAA,CAAS;AAAA,MACxB;AAAA,QAAC;AAAA,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,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAA,IAAW,CAAA;AAAA,UAClD,GAAI,QAAQ,OAAO,EAAE,eAAe,QAAQ,KAAA,IAAS,CAAA;AAAA,UACrD,GAAI,aAAa,EAAE,WAAW,WAAA,IAAe,CAAA;AAAA,UAC7C,IAAI,+CAAe,YAChB;AAAA,YACE,oBAAoB,kBAAkB,cAAc,QAAQ;AAAA,UAAA,IAE9D,CAAA;AAAA,QAAC;AAAA,MAAA;AAAA,IACP,EAAA,CACF;AAAA,EAAA,GAEJ;AAEJ;"}
|
|
@@ -8,8 +8,19 @@ const ToolCall = require("./ToolCall.cjs");
|
|
|
8
8
|
const MessageActions = require("./MessageActions.cjs");
|
|
9
9
|
const Reasoning = require("./Reasoning.cjs");
|
|
10
10
|
const types = require("./types.cjs");
|
|
11
|
+
const forkSeed = require("./fork-seed.cjs");
|
|
12
|
+
const UiGitBranch = require("../../icons/components/UiGitBranch.cjs");
|
|
11
13
|
const UiFile = require("../../icons/components/UiFile.cjs");
|
|
12
|
-
function Message({
|
|
14
|
+
function Message({
|
|
15
|
+
message,
|
|
16
|
+
className,
|
|
17
|
+
onRegenerate,
|
|
18
|
+
onApprove,
|
|
19
|
+
renderToolResult
|
|
20
|
+
}) {
|
|
21
|
+
if (forkSeed.isForkSeedMessage(message)) {
|
|
22
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ForkSeedMessage, { message, className });
|
|
23
|
+
}
|
|
13
24
|
const isUser = message.role === "user";
|
|
14
25
|
const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
15
26
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -51,6 +62,30 @@ function Message({ message, className, onRegenerate, onApprove, renderToolResult
|
|
|
51
62
|
}
|
|
52
63
|
);
|
|
53
64
|
}
|
|
65
|
+
function ForkSeedMessage({
|
|
66
|
+
message,
|
|
67
|
+
className
|
|
68
|
+
}) {
|
|
69
|
+
const provenance = forkSeed.forkSeedProvenance(message);
|
|
70
|
+
const transcript = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
71
|
+
const label = provenance.title ? `Forked from ${provenance.title}` : "Forked from another conversation";
|
|
72
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
73
|
+
"details",
|
|
74
|
+
{
|
|
75
|
+
className: utils.cn(
|
|
76
|
+
"group/fork max-w-full self-center rounded-full border border-border bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground open:w-full open:rounded-lg",
|
|
77
|
+
className
|
|
78
|
+
),
|
|
79
|
+
children: [
|
|
80
|
+
/* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-center gap-1.5 font-medium text-foreground/80", children: [
|
|
81
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon.Icon, { icon: UiGitBranch.UiGitBranch, className: "size-3.5" }),
|
|
82
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label })
|
|
83
|
+
] }),
|
|
84
|
+
transcript && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap border-t border-border pt-2 font-mono text-[11px] text-muted-foreground", children: transcript })
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
);
|
|
88
|
+
}
|
|
54
89
|
function MessagePart({
|
|
55
90
|
part,
|
|
56
91
|
isUser,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.cjs","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n type ToolResultRenderer,\n} from \"./types\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove
|
|
1
|
+
{"version":3,"file":"Message.cjs","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile, UiGitBranch } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n type ToolResultRenderer,\n} from \"./types\";\nimport { forkSeedProvenance, isForkSeedMessage } from \"./fork-seed\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove?:\n | ((approvalId: string, approved: boolean, reason?: string) => void)\n | undefined;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\n};\n\nexport type MessageProps = MessageActionHandlers & {\n message: UIMessage;\n className?: string;\n};\n\n/** Renders one chat message. User messages are right-aligned bubbles; assistant\n * messages render text as markdown, reasoning and tool parts inline, file parts\n * as thumbnails/chips, and a hover action row (copy / regenerate). */\nexport function Message({\n message,\n className,\n onRegenerate,\n onApprove,\n renderToolResult,\n}: MessageProps) {\n if (isForkSeedMessage(message)) {\n return <ForkSeedMessage message={message} className={className} />;\n }\n const isUser = message.role === \"user\";\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\");\n\n return (\n <div\n className={cn(\n \"group flex w-full max-w-[95%] flex-col gap-2\",\n isUser ? \"ml-auto items-end\" : \"items-start\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm\",\n isUser &&\n \"rounded-lg bg-secondary px-4 py-3 text-secondary-foreground\",\n )}\n >\n {message.parts.map((part, i) => (\n <MessagePart\n key={`${message.id}-${i}`}\n part={part}\n isUser={isUser}\n onApprove={onApprove}\n renderToolResult={renderToolResult}\n />\n ))}\n </div>\n\n {!isUser && text && (\n <MessageActions\n text={text}\n onRegenerate={\n onRegenerate ? () => onRegenerate(message.id) : undefined\n }\n />\n )}\n </div>\n );\n}\n\nfunction ForkSeedMessage({\n message,\n className,\n}: {\n message: UIMessage;\n className?: string | undefined;\n}) {\n const provenance = forkSeedProvenance(message);\n const transcript = message.parts\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\\n\");\n const label = provenance.title\n ? `Forked from ${provenance.title}`\n : \"Forked from another conversation\";\n return (\n <details\n className={cn(\n \"group/fork max-w-full self-center rounded-full border border-border bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground open:w-full open:rounded-lg\",\n className,\n )}\n >\n <summary className=\"flex cursor-pointer list-none items-center justify-center gap-1.5 font-medium text-foreground/80\">\n <Icon icon={UiGitBranch} className=\"size-3.5\" />\n <span>{label}</span>\n </summary>\n {transcript && (\n <pre className=\"mt-2 max-h-64 overflow-auto whitespace-pre-wrap border-t border-border pt-2 font-mono text-[11px] text-muted-foreground\">\n {transcript}\n </pre>\n )}\n </details>\n );\n}\n\nfunction MessagePart({\n part,\n isUser,\n onApprove,\n renderToolResult,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n renderToolResult: MessageActionHandlers[\"renderToolResult\"];\n}) {\n if (part.type === \"text\") {\n if (isUser) {\n return (\n <span className=\"whitespace-pre-wrap break-words\">{part.text}</span>\n );\n }\n return <Markdown text={part.text} />;\n }\n if (isReasoningPart(part)) {\n return <Reasoning text={part.text} />;\n }\n if (isFilePart(part)) {\n return <FilePart part={part} />;\n }\n if (isDynamicToolPart(part) || isTypedToolPart(part)) {\n return (\n <ToolCall\n part={part as AnyToolPart}\n onApprove={onApprove}\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n );\n }\n return null;\n}\n\n/** Renders an attachment: images inline as a thumbnail, everything else as a\n * labelled file chip. */\nfunction FilePart({ part }: { part: FileUIPart }) {\n const isImage = part.mediaType?.startsWith(\"image/\");\n if (isImage && part.url) {\n return (\n <img\n src={part.url}\n alt={part.filename ?? \"attachment\"}\n className=\"max-h-48 max-w-full rounded-md border border-border\"\n />\n );\n }\n return (\n <a\n href={part.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFile} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate\">\n {part.filename ?? part.mediaType ?? \"file\"}\n </span>\n </a>\n );\n}\n"],"names":["isForkSeedMessage","jsx","jsxs","cn","MessageActions","forkSeedProvenance","Icon","UiGitBranch","Markdown","isReasoningPart","Reasoning","isFilePart","isDynamicToolPart","isTypedToolPart","ToolCall","UiFile"],"mappings":"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"}
|
|
@@ -15,5 +15,5 @@ export type MessageProps = MessageActionHandlers & {
|
|
|
15
15
|
/** Renders one chat message. User messages are right-aligned bubbles; assistant
|
|
16
16
|
* messages render text as markdown, reasoning and tool parts inline, file parts
|
|
17
17
|
* as thumbnails/chips, and a hover action row (copy / regenerate). */
|
|
18
|
-
export declare function Message({ message, className, onRegenerate, onApprove, renderToolResult }: MessageProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export declare function Message({ message, className, onRegenerate, onApprove, renderToolResult, }: MessageProps): import("react/jsx-runtime").JSX.Element;
|
|
19
19
|
//# sourceMappingURL=Message.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Message.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AACrD,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"Message.d.ts","sourceRoot":"","sources":["../../../src/data/chat/Message.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,SAAS,CAAC;AACrD,OAAO,EAML,KAAK,kBAAkB,EACxB,MAAM,SAAS,CAAC;AAGjB,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAAG;IAClC,2DAA2D;IAC3D,YAAY,CAAC,EAAE,CAAC,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzD,0CAA0C;IAC1C,SAAS,CAAC,EACN,CAAC,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC,GAClE,SAAS,CAAC;IACd,oEAAoE;IACpE,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,qBAAqB,GAAG;IACjD,OAAO,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;uEAEuE;AACvE,wBAAgB,OAAO,CAAC,EACtB,OAAO,EACP,SAAS,EACT,YAAY,EACZ,SAAS,EACT,gBAAgB,GACjB,EAAE,YAAY,2CA8Cd"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { cn } from "../../lib/utils.js";
|
|
3
3
|
import { Icon } from "../Icon.js";
|
|
4
4
|
import { Markdown } from "../Markdown.js";
|
|
@@ -6,8 +6,19 @@ import { ToolCall } from "./ToolCall.js";
|
|
|
6
6
|
import { MessageActions } from "./MessageActions.js";
|
|
7
7
|
import { Reasoning } from "./Reasoning.js";
|
|
8
8
|
import { isReasoningPart, isFilePart, isDynamicToolPart, isTypedToolPart } from "./types.js";
|
|
9
|
+
import { isForkSeedMessage, forkSeedProvenance } from "./fork-seed.js";
|
|
10
|
+
import { UiGitBranch } from "../../icons/components/UiGitBranch.js";
|
|
9
11
|
import { UiFile } from "../../icons/components/UiFile.js";
|
|
10
|
-
function Message({
|
|
12
|
+
function Message({
|
|
13
|
+
message,
|
|
14
|
+
className,
|
|
15
|
+
onRegenerate,
|
|
16
|
+
onApprove,
|
|
17
|
+
renderToolResult
|
|
18
|
+
}) {
|
|
19
|
+
if (isForkSeedMessage(message)) {
|
|
20
|
+
return /* @__PURE__ */ jsx(ForkSeedMessage, { message, className });
|
|
21
|
+
}
|
|
11
22
|
const isUser = message.role === "user";
|
|
12
23
|
const text = message.parts.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
13
24
|
return /* @__PURE__ */ jsxs(
|
|
@@ -49,6 +60,30 @@ function Message({ message, className, onRegenerate, onApprove, renderToolResult
|
|
|
49
60
|
}
|
|
50
61
|
);
|
|
51
62
|
}
|
|
63
|
+
function ForkSeedMessage({
|
|
64
|
+
message,
|
|
65
|
+
className
|
|
66
|
+
}) {
|
|
67
|
+
const provenance = forkSeedProvenance(message);
|
|
68
|
+
const transcript = message.parts.filter((part) => part.type === "text").map((part) => part.text).join("\n");
|
|
69
|
+
const label = provenance.title ? `Forked from ${provenance.title}` : "Forked from another conversation";
|
|
70
|
+
return /* @__PURE__ */ jsxs(
|
|
71
|
+
"details",
|
|
72
|
+
{
|
|
73
|
+
className: cn(
|
|
74
|
+
"group/fork max-w-full self-center rounded-full border border-border bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground open:w-full open:rounded-lg",
|
|
75
|
+
className
|
|
76
|
+
),
|
|
77
|
+
children: [
|
|
78
|
+
/* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer list-none items-center justify-center gap-1.5 font-medium text-foreground/80", children: [
|
|
79
|
+
/* @__PURE__ */ jsx(Icon, { icon: UiGitBranch, className: "size-3.5" }),
|
|
80
|
+
/* @__PURE__ */ jsx("span", { children: label })
|
|
81
|
+
] }),
|
|
82
|
+
transcript && /* @__PURE__ */ jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap border-t border-border pt-2 font-mono text-[11px] text-muted-foreground", children: transcript })
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
52
87
|
function MessagePart({
|
|
53
88
|
part,
|
|
54
89
|
isUser,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Message.js","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n type ToolResultRenderer,\n} from \"./types\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove
|
|
1
|
+
{"version":3,"file":"Message.js","sources":["../../../src/data/chat/Message.tsx"],"sourcesContent":["import { cn } from \"../../lib/utils\";\nimport { Icon } from \"../Icon\";\nimport { UiFile, UiGitBranch } from \"../../icons\";\nimport { Markdown } from \"../Markdown\";\nimport { ToolCall } from \"./ToolCall\";\nimport { MessageActions } from \"./MessageActions\";\nimport { Reasoning } from \"./Reasoning\";\nimport type { UIMessage, FileUIPart } from \"./types\";\nimport {\n isDynamicToolPart,\n isTypedToolPart,\n isReasoningPart,\n isFilePart,\n type AnyToolPart,\n type ToolResultRenderer,\n} from \"./types\";\nimport { forkSeedProvenance, isForkSeedMessage } from \"./fork-seed\";\n\n/** Callbacks the conversation threads down to each message. */\nexport type MessageActionHandlers = {\n /** Re-generate the assistant message with the given id. */\n onRegenerate?: ((messageId: string) => void) | undefined;\n /** Respond to a tool approval request. */\n onApprove?:\n | ((approvalId: string, approved: boolean, reason?: string) => void)\n | undefined;\n /** Optional host renderer for recognized completed tool outputs. */\n renderToolResult?: ToolResultRenderer;\n};\n\nexport type MessageProps = MessageActionHandlers & {\n message: UIMessage;\n className?: string;\n};\n\n/** Renders one chat message. User messages are right-aligned bubbles; assistant\n * messages render text as markdown, reasoning and tool parts inline, file parts\n * as thumbnails/chips, and a hover action row (copy / regenerate). */\nexport function Message({\n message,\n className,\n onRegenerate,\n onApprove,\n renderToolResult,\n}: MessageProps) {\n if (isForkSeedMessage(message)) {\n return <ForkSeedMessage message={message} className={className} />;\n }\n const isUser = message.role === \"user\";\n const text = message.parts\n .filter((p) => p.type === \"text\")\n .map((p) => (p as { text: string }).text)\n .join(\"\");\n\n return (\n <div\n className={cn(\n \"group flex w-full max-w-[95%] flex-col gap-2\",\n isUser ? \"ml-auto items-end\" : \"items-start\",\n className,\n )}\n >\n <div\n className={cn(\n \"flex w-fit min-w-0 max-w-full flex-col gap-2 overflow-hidden text-sm\",\n isUser &&\n \"rounded-lg bg-secondary px-4 py-3 text-secondary-foreground\",\n )}\n >\n {message.parts.map((part, i) => (\n <MessagePart\n key={`${message.id}-${i}`}\n part={part}\n isUser={isUser}\n onApprove={onApprove}\n renderToolResult={renderToolResult}\n />\n ))}\n </div>\n\n {!isUser && text && (\n <MessageActions\n text={text}\n onRegenerate={\n onRegenerate ? () => onRegenerate(message.id) : undefined\n }\n />\n )}\n </div>\n );\n}\n\nfunction ForkSeedMessage({\n message,\n className,\n}: {\n message: UIMessage;\n className?: string | undefined;\n}) {\n const provenance = forkSeedProvenance(message);\n const transcript = message.parts\n .filter((part) => part.type === \"text\")\n .map((part) => part.text)\n .join(\"\\n\");\n const label = provenance.title\n ? `Forked from ${provenance.title}`\n : \"Forked from another conversation\";\n return (\n <details\n className={cn(\n \"group/fork max-w-full self-center rounded-full border border-border bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground open:w-full open:rounded-lg\",\n className,\n )}\n >\n <summary className=\"flex cursor-pointer list-none items-center justify-center gap-1.5 font-medium text-foreground/80\">\n <Icon icon={UiGitBranch} className=\"size-3.5\" />\n <span>{label}</span>\n </summary>\n {transcript && (\n <pre className=\"mt-2 max-h-64 overflow-auto whitespace-pre-wrap border-t border-border pt-2 font-mono text-[11px] text-muted-foreground\">\n {transcript}\n </pre>\n )}\n </details>\n );\n}\n\nfunction MessagePart({\n part,\n isUser,\n onApprove,\n renderToolResult,\n}: {\n part: UIMessage[\"parts\"][number];\n isUser: boolean;\n onApprove: MessageActionHandlers[\"onApprove\"];\n renderToolResult: MessageActionHandlers[\"renderToolResult\"];\n}) {\n if (part.type === \"text\") {\n if (isUser) {\n return (\n <span className=\"whitespace-pre-wrap break-words\">{part.text}</span>\n );\n }\n return <Markdown text={part.text} />;\n }\n if (isReasoningPart(part)) {\n return <Reasoning text={part.text} />;\n }\n if (isFilePart(part)) {\n return <FilePart part={part} />;\n }\n if (isDynamicToolPart(part) || isTypedToolPart(part)) {\n return (\n <ToolCall\n part={part as AnyToolPart}\n onApprove={onApprove}\n {...(renderToolResult ? { renderToolResult } : {})}\n />\n );\n }\n return null;\n}\n\n/** Renders an attachment: images inline as a thumbnail, everything else as a\n * labelled file chip. */\nfunction FilePart({ part }: { part: FileUIPart }) {\n const isImage = part.mediaType?.startsWith(\"image/\");\n if (isImage && part.url) {\n return (\n <img\n src={part.url}\n alt={part.filename ?? \"attachment\"}\n className=\"max-h-48 max-w-full rounded-md border border-border\"\n />\n );\n }\n return (\n <a\n href={part.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs text-muted-foreground hover:text-foreground\"\n >\n <Icon icon={UiFile} className=\"size-3.5 shrink-0\" />\n <span className=\"truncate\">\n {part.filename ?? part.mediaType ?? \"file\"}\n </span>\n </a>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,22 @@ async function getChatSession(sessionsApi, sessionId, fetcher = fetch) {
|
|
|
12
12
|
}
|
|
13
13
|
return parseSession(await response.json());
|
|
14
14
|
}
|
|
15
|
+
async function forkChatSession(sessionsApi, sessionId, fetcher = fetch) {
|
|
16
|
+
if (!sessionId.trim()) {
|
|
17
|
+
throw new Error("A session id is required to fork this chat.");
|
|
18
|
+
}
|
|
19
|
+
const response = await fetcher(
|
|
20
|
+
`${sessionEndpoint(sessionsApi, sessionId)}/fork`,
|
|
21
|
+
{
|
|
22
|
+
method: "POST",
|
|
23
|
+
headers: { Accept: "application/json" }
|
|
24
|
+
}
|
|
25
|
+
);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw await responseError("Fork chat session", response);
|
|
28
|
+
}
|
|
29
|
+
return parseSession(await response.json());
|
|
30
|
+
}
|
|
15
31
|
async function postToolApproval(decision, fetcher = fetch) {
|
|
16
32
|
if (!decision.sessionId.trim()) {
|
|
17
33
|
throw new Error("A session id is required to resolve this tool approval.");
|
|
@@ -59,6 +75,7 @@ async function responseError(label, response) {
|
|
|
59
75
|
`${label} failed with status ${response.status}${detail ? `: ${detail}` : "."}`
|
|
60
76
|
);
|
|
61
77
|
}
|
|
78
|
+
exports.forkChatSession = forkChatSession;
|
|
62
79
|
exports.getChatSession = getChatSession;
|
|
63
80
|
exports.postToolApproval = postToolApproval;
|
|
64
81
|
//# sourceMappingURL=approval.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.cjs","sources":["../../../src/data/chat/approval.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport type CaptainChatSession = {\n id: string;\n revision?: number;\n messages: UIMessage[];\n};\n\nexport type ToolApprovalDecision = {\n sessionsApi: string;\n sessionId: string;\n approvalId: string;\n approved: boolean;\n reason?: string;\n};\n\ntype SessionFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport async function getChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to load this chat.\");\n }\n const response = await fetcher(sessionEndpoint(sessionsApi, sessionId), {\n headers: { Accept: \"application/json\" },\n });\n if (!response.ok) {\n throw await responseError(\"Chat session request\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function postToolApproval(\n decision: ToolApprovalDecision,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!decision.sessionId.trim()) {\n throw new Error(\"A session id is required to resolve this tool approval.\");\n }\n const endpoint = `${sessionEndpoint(\n decision.sessionsApi,\n decision.sessionId,\n )}/approvals/${encodeURIComponent(decision.approvalId)}`;\n const response = await fetcher(endpoint, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(\n decision.reason\n ? { approved: decision.approved, reason: decision.reason }\n : { approved: decision.approved },\n ),\n });\n if (!response.ok) {\n throw await responseError(\"Tool approval\", response);\n }\n return parseSession(await response.json());\n}\n\nfunction sessionEndpoint(sessionsApi: string, sessionId: string): string {\n return `${stripTrailingSlashes(sessionsApi)}/${encodeURIComponent(sessionId)}`;\n}\n\n// Linear scan instead of `.replace(/\\/+$/, \"\")`: the regex backtracks\n// polynomially on caller-supplied base URLs with many trailing slashes.\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end--;\n return value.slice(0, end);\n}\n\nfunction parseSession(value: unknown): CaptainChatSession {\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"id\" in value) ||\n typeof value.id !== \"string\"\n ) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n const messages = \"messages\" in value ? value.messages : [];\n if (!Array.isArray(messages)) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n return { ...value, messages } as CaptainChatSession;\n}\n\nasync function responseError(\n label: string,\n response: Response,\n): Promise<Error> {\n const detail = (await response.text()).trim();\n return new Error(\n `${label} failed with status ${response.status}${\n detail ? `: ${detail}` : \".\"\n }`,\n );\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"approval.cjs","sources":["../../../src/data/chat/approval.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\nimport type { ChatModelRuntime } from \"./types\";\n\nexport type CaptainChatSession = {\n id: string;\n revision?: number;\n messages: UIMessage[];\n providerSessionId?: string;\n runtime?: ChatModelRuntime;\n forkedFrom?: string;\n};\n\nexport type ToolApprovalDecision = {\n sessionsApi: string;\n sessionId: string;\n approvalId: string;\n approved: boolean;\n reason?: string;\n};\n\ntype SessionFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport async function getChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to load this chat.\");\n }\n const response = await fetcher(sessionEndpoint(sessionsApi, sessionId), {\n headers: { Accept: \"application/json\" },\n });\n if (!response.ok) {\n throw await responseError(\"Chat session request\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function forkChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to fork this chat.\");\n }\n const response = await fetcher(\n `${sessionEndpoint(sessionsApi, sessionId)}/fork`,\n {\n method: \"POST\",\n headers: { Accept: \"application/json\" },\n },\n );\n if (!response.ok) {\n throw await responseError(\"Fork chat session\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function postToolApproval(\n decision: ToolApprovalDecision,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!decision.sessionId.trim()) {\n throw new Error(\"A session id is required to resolve this tool approval.\");\n }\n const endpoint = `${sessionEndpoint(\n decision.sessionsApi,\n decision.sessionId,\n )}/approvals/${encodeURIComponent(decision.approvalId)}`;\n const response = await fetcher(endpoint, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(\n decision.reason\n ? { approved: decision.approved, reason: decision.reason }\n : { approved: decision.approved },\n ),\n });\n if (!response.ok) {\n throw await responseError(\"Tool approval\", response);\n }\n return parseSession(await response.json());\n}\n\nfunction sessionEndpoint(sessionsApi: string, sessionId: string): string {\n return `${stripTrailingSlashes(sessionsApi)}/${encodeURIComponent(sessionId)}`;\n}\n\n// Linear scan instead of `.replace(/\\/+$/, \"\")`: the regex backtracks\n// polynomially on caller-supplied base URLs with many trailing slashes.\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end--;\n return value.slice(0, end);\n}\n\nfunction parseSession(value: unknown): CaptainChatSession {\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"id\" in value) ||\n typeof value.id !== \"string\"\n ) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n const messages = \"messages\" in value ? value.messages : [];\n if (!Array.isArray(messages)) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n return { ...value, messages } as CaptainChatSession;\n}\n\nasync function responseError(\n label: string,\n response: Response,\n): Promise<Error> {\n const detail = (await response.text()).trim();\n return new Error(\n `${label} failed with status ${response.status}${\n detail ? `: ${detail}` : \".\"\n }`,\n );\n}\n"],"names":[],"mappings":";;AAyBA,eAAsB,eACpB,aACA,WACA,UAAwB,OACK;AAC7B,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,QAAM,WAAW,MAAM,QAAQ,gBAAgB,aAAa,SAAS,GAAG;AAAA,IACtE,SAAS,EAAE,QAAQ,mBAAA;AAAA,EAAmB,CACvC;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,wBAAwB,QAAQ;AAAA,EAC5D;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,eAAsB,gBACpB,aACA,WACA,UAAwB,OACK;AAC7B,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,QAAM,WAAW,MAAM;AAAA,IACrB,GAAG,gBAAgB,aAAa,SAAS,CAAC;AAAA,IAC1C;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,EAAE,QAAQ,mBAAA;AAAA,IAAmB;AAAA,EACxC;AAEF,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,qBAAqB,QAAQ;AAAA,EACzD;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,eAAsB,iBACpB,UACA,UAAwB,OACK;AAC7B,MAAI,CAAC,SAAS,UAAU,QAAQ;AAC9B,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,QAAM,WAAW,GAAG;AAAA,IAClB,SAAS;AAAA,IACT,SAAS;AAAA,EAAA,CACV,cAAc,mBAAmB,SAAS,UAAU,CAAC;AACtD,QAAM,WAAW,MAAM,QAAQ,UAAU;AAAA,IACvC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,MAAM,KAAK;AAAA,MACT,SAAS,SACL,EAAE,UAAU,SAAS,UAAU,QAAQ,SAAS,OAAA,IAChD,EAAE,UAAU,SAAS,SAAA;AAAA,IAAS;AAAA,EACpC,CACD;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,iBAAiB,QAAQ;AAAA,EACrD;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,SAAS,gBAAgB,aAAqB,WAA2B;AACvE,SAAO,GAAG,qBAAqB,WAAW,CAAC,IAAI,mBAAmB,SAAS,CAAC;AAC9E;AAIA,SAAS,qBAAqB,OAAuB;AACnD,MAAI,MAAM,MAAM;AAChB,SAAO,MAAM,KAAK,MAAM,MAAM,CAAC,MAAM,IAAK;AAC1C,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,SAAS,aAAa,OAAoC;AACxD,MACE,OAAO,UAAU,YACjB,UAAU,QACV,EAAE,QAAQ,UACV,OAAO,MAAM,OAAO,UACpB;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,WAAW,cAAc,QAAQ,MAAM,WAAW,CAAA;AACxD,MAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;AAC5B,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,SAAO,EAAE,GAAG,OAAO,SAAA;AACrB;AAEA,eAAe,cACb,OACA,UACgB;AAChB,QAAM,UAAU,MAAM,SAAS,KAAA,GAAQ,KAAA;AACvC,SAAO,IAAI;AAAA,IACT,GAAG,KAAK,uBAAuB,SAAS,MAAM,GAC5C,SAAS,KAAK,MAAM,KAAK,GAC3B;AAAA,EAAA;AAEJ;;;;"}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { UIMessage } from 'ai';
|
|
2
|
+
import { ChatModelRuntime } from './types';
|
|
2
3
|
export type CaptainChatSession = {
|
|
3
4
|
id: string;
|
|
4
5
|
revision?: number;
|
|
5
6
|
messages: UIMessage[];
|
|
7
|
+
providerSessionId?: string;
|
|
8
|
+
runtime?: ChatModelRuntime;
|
|
9
|
+
forkedFrom?: string;
|
|
6
10
|
};
|
|
7
11
|
export type ToolApprovalDecision = {
|
|
8
12
|
sessionsApi: string;
|
|
@@ -13,6 +17,7 @@ export type ToolApprovalDecision = {
|
|
|
13
17
|
};
|
|
14
18
|
type SessionFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
15
19
|
export declare function getChatSession(sessionsApi: string, sessionId: string, fetcher?: SessionFetch): Promise<CaptainChatSession>;
|
|
20
|
+
export declare function forkChatSession(sessionsApi: string, sessionId: string, fetcher?: SessionFetch): Promise<CaptainChatSession>;
|
|
16
21
|
export declare function postToolApproval(decision: ToolApprovalDecision, fetcher?: SessionFetch): Promise<CaptainChatSession>;
|
|
17
22
|
export {};
|
|
18
23
|
//# sourceMappingURL=approval.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../../src/data/chat/approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"approval.d.ts","sourceRoot":"","sources":["../../../src/data/chat/approval.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,OAAO,CAAC,EAAE,gBAAgB,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,KAAK,YAAY,GAAG,CAClB,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEvB,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,YAAoB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAW7B;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,YAAoB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAe7B;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,GAAE,YAAoB,GAC5B,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
|
|
@@ -10,6 +10,22 @@ async function getChatSession(sessionsApi, sessionId, fetcher = fetch) {
|
|
|
10
10
|
}
|
|
11
11
|
return parseSession(await response.json());
|
|
12
12
|
}
|
|
13
|
+
async function forkChatSession(sessionsApi, sessionId, fetcher = fetch) {
|
|
14
|
+
if (!sessionId.trim()) {
|
|
15
|
+
throw new Error("A session id is required to fork this chat.");
|
|
16
|
+
}
|
|
17
|
+
const response = await fetcher(
|
|
18
|
+
`${sessionEndpoint(sessionsApi, sessionId)}/fork`,
|
|
19
|
+
{
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { Accept: "application/json" }
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
if (!response.ok) {
|
|
25
|
+
throw await responseError("Fork chat session", response);
|
|
26
|
+
}
|
|
27
|
+
return parseSession(await response.json());
|
|
28
|
+
}
|
|
13
29
|
async function postToolApproval(decision, fetcher = fetch) {
|
|
14
30
|
if (!decision.sessionId.trim()) {
|
|
15
31
|
throw new Error("A session id is required to resolve this tool approval.");
|
|
@@ -58,6 +74,7 @@ async function responseError(label, response) {
|
|
|
58
74
|
);
|
|
59
75
|
}
|
|
60
76
|
export {
|
|
77
|
+
forkChatSession,
|
|
61
78
|
getChatSession,
|
|
62
79
|
postToolApproval
|
|
63
80
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"approval.js","sources":["../../../src/data/chat/approval.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport type CaptainChatSession = {\n id: string;\n revision?: number;\n messages: UIMessage[];\n};\n\nexport type ToolApprovalDecision = {\n sessionsApi: string;\n sessionId: string;\n approvalId: string;\n approved: boolean;\n reason?: string;\n};\n\ntype SessionFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport async function getChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to load this chat.\");\n }\n const response = await fetcher(sessionEndpoint(sessionsApi, sessionId), {\n headers: { Accept: \"application/json\" },\n });\n if (!response.ok) {\n throw await responseError(\"Chat session request\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function postToolApproval(\n decision: ToolApprovalDecision,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!decision.sessionId.trim()) {\n throw new Error(\"A session id is required to resolve this tool approval.\");\n }\n const endpoint = `${sessionEndpoint(\n decision.sessionsApi,\n decision.sessionId,\n )}/approvals/${encodeURIComponent(decision.approvalId)}`;\n const response = await fetcher(endpoint, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(\n decision.reason\n ? { approved: decision.approved, reason: decision.reason }\n : { approved: decision.approved },\n ),\n });\n if (!response.ok) {\n throw await responseError(\"Tool approval\", response);\n }\n return parseSession(await response.json());\n}\n\nfunction sessionEndpoint(sessionsApi: string, sessionId: string): string {\n return `${stripTrailingSlashes(sessionsApi)}/${encodeURIComponent(sessionId)}`;\n}\n\n// Linear scan instead of `.replace(/\\/+$/, \"\")`: the regex backtracks\n// polynomially on caller-supplied base URLs with many trailing slashes.\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end--;\n return value.slice(0, end);\n}\n\nfunction parseSession(value: unknown): CaptainChatSession {\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"id\" in value) ||\n typeof value.id !== \"string\"\n ) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n const messages = \"messages\" in value ? value.messages : [];\n if (!Array.isArray(messages)) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n return { ...value, messages } as CaptainChatSession;\n}\n\nasync function responseError(\n label: string,\n response: Response,\n): Promise<Error> {\n const detail = (await response.text()).trim();\n return new Error(\n `${label} failed with status ${response.status}${\n detail ? `: ${detail}` : \".\"\n }`,\n );\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"approval.js","sources":["../../../src/data/chat/approval.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\nimport type { ChatModelRuntime } from \"./types\";\n\nexport type CaptainChatSession = {\n id: string;\n revision?: number;\n messages: UIMessage[];\n providerSessionId?: string;\n runtime?: ChatModelRuntime;\n forkedFrom?: string;\n};\n\nexport type ToolApprovalDecision = {\n sessionsApi: string;\n sessionId: string;\n approvalId: string;\n approved: boolean;\n reason?: string;\n};\n\ntype SessionFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\nexport async function getChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to load this chat.\");\n }\n const response = await fetcher(sessionEndpoint(sessionsApi, sessionId), {\n headers: { Accept: \"application/json\" },\n });\n if (!response.ok) {\n throw await responseError(\"Chat session request\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function forkChatSession(\n sessionsApi: string,\n sessionId: string,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!sessionId.trim()) {\n throw new Error(\"A session id is required to fork this chat.\");\n }\n const response = await fetcher(\n `${sessionEndpoint(sessionsApi, sessionId)}/fork`,\n {\n method: \"POST\",\n headers: { Accept: \"application/json\" },\n },\n );\n if (!response.ok) {\n throw await responseError(\"Fork chat session\", response);\n }\n return parseSession(await response.json());\n}\n\nexport async function postToolApproval(\n decision: ToolApprovalDecision,\n fetcher: SessionFetch = fetch,\n): Promise<CaptainChatSession> {\n if (!decision.sessionId.trim()) {\n throw new Error(\"A session id is required to resolve this tool approval.\");\n }\n const endpoint = `${sessionEndpoint(\n decision.sessionsApi,\n decision.sessionId,\n )}/approvals/${encodeURIComponent(decision.approvalId)}`;\n const response = await fetcher(endpoint, {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(\n decision.reason\n ? { approved: decision.approved, reason: decision.reason }\n : { approved: decision.approved },\n ),\n });\n if (!response.ok) {\n throw await responseError(\"Tool approval\", response);\n }\n return parseSession(await response.json());\n}\n\nfunction sessionEndpoint(sessionsApi: string, sessionId: string): string {\n return `${stripTrailingSlashes(sessionsApi)}/${encodeURIComponent(sessionId)}`;\n}\n\n// Linear scan instead of `.replace(/\\/+$/, \"\")`: the regex backtracks\n// polynomially on caller-supplied base URLs with many trailing slashes.\nfunction stripTrailingSlashes(value: string): string {\n let end = value.length;\n while (end > 0 && value[end - 1] === \"/\") end--;\n return value.slice(0, end);\n}\n\nfunction parseSession(value: unknown): CaptainChatSession {\n if (\n typeof value !== \"object\" ||\n value === null ||\n !(\"id\" in value) ||\n typeof value.id !== \"string\"\n ) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n const messages = \"messages\" in value ? value.messages : [];\n if (!Array.isArray(messages)) {\n throw new Error(\"Captain chat session response is invalid.\");\n }\n return { ...value, messages } as CaptainChatSession;\n}\n\nasync function responseError(\n label: string,\n response: Response,\n): Promise<Error> {\n const detail = (await response.text()).trim();\n return new Error(\n `${label} failed with status ${response.status}${\n detail ? `: ${detail}` : \".\"\n }`,\n );\n}\n"],"names":[],"mappings":"AAyBA,eAAsB,eACpB,aACA,WACA,UAAwB,OACK;AAC7B,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,QAAM,WAAW,MAAM,QAAQ,gBAAgB,aAAa,SAAS,GAAG;AAAA,IACtE,SAAS,EAAE,QAAQ,mBAAA;AAAA,EAAmB,CACvC;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,wBAAwB,QAAQ;AAAA,EAC5D;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,eAAsB,gBACpB,aACA,WACA,UAAwB,OACK;AAC7B,MAAI,CAAC,UAAU,QAAQ;AACrB,UAAM,IAAI,MAAM,6CAA6C;AAAA,EAC/D;AACA,QAAM,WAAW,MAAM;AAAA,IACrB,GAAG,gBAAgB,aAAa,SAAS,CAAC;AAAA,IAC1C;AAAA,MACE,QAAQ;AAAA,MACR,SAAS,EAAE,QAAQ,mBAAA;AAAA,IAAmB;AAAA,EACxC;AAEF,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,qBAAqB,QAAQ;AAAA,EACzD;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,eAAsB,iBACpB,UACA,UAAwB,OACK;AAC7B,MAAI,CAAC,SAAS,UAAU,QAAQ;AAC9B,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC3E;AACA,QAAM,WAAW,GAAG;AAAA,IAClB,SAAS;AAAA,IACT,SAAS;AAAA,EAAA,CACV,cAAc,mBAAmB,SAAS,UAAU,CAAC;AACtD,QAAM,WAAW,MAAM,QAAQ,UAAU;AAAA,IACvC,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,gBAAgB;AAAA,IAAA;AAAA,IAElB,MAAM,KAAK;AAAA,MACT,SAAS,SACL,EAAE,UAAU,SAAS,UAAU,QAAQ,SAAS,OAAA,IAChD,EAAE,UAAU,SAAS,SAAA;AAAA,IAAS;AAAA,EACpC,CACD;AACD,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,MAAM,cAAc,iBAAiB,QAAQ;AAAA,EACrD;AACA,SAAO,aAAa,MAAM,SAAS,MAAM;AAC3C;AAEA,SAAS,gBAAgB,aAAqB,WAA2B;AACvE,SAAO,GAAG,qBAAqB,WAAW,CAAC,IAAI,mBAAmB,SAAS,CAAC;AAC9E;AAIA,SAAS,qBAAqB,OAAuB;AACnD,MAAI,MAAM,MAAM;AAChB,SAAO,MAAM,KAAK,MAAM,MAAM,CAAC,MAAM,IAAK;AAC1C,SAAO,MAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,SAAS,aAAa,OAAoC;AACxD,MACE,OAAO,UAAU,YACjB,UAAU,QACV,EAAE,QAAQ,UACV,OAAO,MAAM,OAAO,UACpB;AACA,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,QAAM,WAAW,cAAc,QAAQ,MAAM,WAAW,CAAA;AACxD,MAAI,CAAC,MAAM,QAAQ,QAAQ,GAAG;AAC5B,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AACA,SAAO,EAAE,GAAG,OAAO,SAAA;AACrB;AAEA,eAAe,cACb,OACA,UACgB;AAChB,QAAM,UAAU,MAAM,SAAS,KAAA,GAAQ,KAAA;AACvC,SAAO,IAAI;AAAA,IACT,GAAG,KAAK,uBAAuB,SAAS,MAAM,GAC5C,SAAS,KAAK,MAAM,KAAK,GAC3B;AAAA,EAAA;AAEJ;"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const FORK_SEED_PART_TYPE = "data-fork-seed";
|
|
4
|
+
function isForkSeedMessage(message) {
|
|
5
|
+
return message.parts.some((part) => part.type === FORK_SEED_PART_TYPE);
|
|
6
|
+
}
|
|
7
|
+
function forkSeedProvenance(message) {
|
|
8
|
+
const marker = message.parts.find(
|
|
9
|
+
(part) => part.type === FORK_SEED_PART_TYPE
|
|
10
|
+
);
|
|
11
|
+
if (!marker || typeof marker.data !== "object" || marker.data === null)
|
|
12
|
+
return {};
|
|
13
|
+
const data = marker.data;
|
|
14
|
+
return {
|
|
15
|
+
...typeof data.forkedFrom === "string" ? { forkedFrom: data.forkedFrom } : {},
|
|
16
|
+
...typeof data.title === "string" ? { title: data.title } : {}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
exports.FORK_SEED_PART_TYPE = FORK_SEED_PART_TYPE;
|
|
20
|
+
exports.forkSeedProvenance = forkSeedProvenance;
|
|
21
|
+
exports.isForkSeedMessage = isForkSeedMessage;
|
|
22
|
+
//# sourceMappingURL=fork-seed.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork-seed.cjs","sources":["../../../src/data/chat/fork-seed.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport const FORK_SEED_PART_TYPE = \"data-fork-seed\";\n\nexport function isForkSeedMessage(message: Pick<UIMessage, \"parts\">): boolean {\n return message.parts.some((part) => part.type === FORK_SEED_PART_TYPE);\n}\n\nexport function forkSeedProvenance(message: Pick<UIMessage, \"parts\">): {\n forkedFrom?: string;\n title?: string;\n} {\n const marker = message.parts.find(\n (part) => part.type === FORK_SEED_PART_TYPE,\n ) as { data?: unknown } | undefined;\n if (!marker || typeof marker.data !== \"object\" || marker.data === null)\n return {};\n const data = marker.data as Record<string, unknown>;\n return {\n ...(typeof data.forkedFrom === \"string\"\n ? { forkedFrom: data.forkedFrom }\n : {}),\n ...(typeof data.title === \"string\" ? { title: data.title } : {}),\n };\n}\n"],"names":[],"mappings":";;AAEO,MAAM,sBAAsB;AAE5B,SAAS,kBAAkB,SAA4C;AAC5E,SAAO,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,mBAAmB;AACvE;AAEO,SAAS,mBAAmB,SAGjC;AACA,QAAM,SAAS,QAAQ,MAAM;AAAA,IAC3B,CAAC,SAAS,KAAK,SAAS;AAAA,EAAA;AAE1B,MAAI,CAAC,UAAU,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS;AAChE,WAAO,CAAA;AACT,QAAM,OAAO,OAAO;AACpB,SAAO;AAAA,IACL,GAAI,OAAO,KAAK,eAAe,WAC3B,EAAE,YAAY,KAAK,WAAA,IACnB,CAAA;AAAA,IACJ,GAAI,OAAO,KAAK,UAAU,WAAW,EAAE,OAAO,KAAK,UAAU,CAAA;AAAA,EAAC;AAElE;;;;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { UIMessage } from 'ai';
|
|
2
|
+
export declare const FORK_SEED_PART_TYPE = "data-fork-seed";
|
|
3
|
+
export declare function isForkSeedMessage(message: Pick<UIMessage, "parts">): boolean;
|
|
4
|
+
export declare function forkSeedProvenance(message: Pick<UIMessage, "parts">): {
|
|
5
|
+
forkedFrom?: string;
|
|
6
|
+
title?: string;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=fork-seed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork-seed.d.ts","sourceRoot":"","sources":["../../../src/data/chat/fork-seed.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,eAAO,MAAM,mBAAmB,mBAAmB,CAAC;AAEpD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,OAAO,CAE5E;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAaA"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const FORK_SEED_PART_TYPE = "data-fork-seed";
|
|
2
|
+
function isForkSeedMessage(message) {
|
|
3
|
+
return message.parts.some((part) => part.type === FORK_SEED_PART_TYPE);
|
|
4
|
+
}
|
|
5
|
+
function forkSeedProvenance(message) {
|
|
6
|
+
const marker = message.parts.find(
|
|
7
|
+
(part) => part.type === FORK_SEED_PART_TYPE
|
|
8
|
+
);
|
|
9
|
+
if (!marker || typeof marker.data !== "object" || marker.data === null)
|
|
10
|
+
return {};
|
|
11
|
+
const data = marker.data;
|
|
12
|
+
return {
|
|
13
|
+
...typeof data.forkedFrom === "string" ? { forkedFrom: data.forkedFrom } : {},
|
|
14
|
+
...typeof data.title === "string" ? { title: data.title } : {}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export {
|
|
18
|
+
FORK_SEED_PART_TYPE,
|
|
19
|
+
forkSeedProvenance,
|
|
20
|
+
isForkSeedMessage
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=fork-seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork-seed.js","sources":["../../../src/data/chat/fork-seed.ts"],"sourcesContent":["import type { UIMessage } from \"ai\";\n\nexport const FORK_SEED_PART_TYPE = \"data-fork-seed\";\n\nexport function isForkSeedMessage(message: Pick<UIMessage, \"parts\">): boolean {\n return message.parts.some((part) => part.type === FORK_SEED_PART_TYPE);\n}\n\nexport function forkSeedProvenance(message: Pick<UIMessage, \"parts\">): {\n forkedFrom?: string;\n title?: string;\n} {\n const marker = message.parts.find(\n (part) => part.type === FORK_SEED_PART_TYPE,\n ) as { data?: unknown } | undefined;\n if (!marker || typeof marker.data !== \"object\" || marker.data === null)\n return {};\n const data = marker.data as Record<string, unknown>;\n return {\n ...(typeof data.forkedFrom === \"string\"\n ? { forkedFrom: data.forkedFrom }\n : {}),\n ...(typeof data.title === \"string\" ? { title: data.title } : {}),\n };\n}\n"],"names":[],"mappings":"AAEO,MAAM,sBAAsB;AAE5B,SAAS,kBAAkB,SAA4C;AAC5E,SAAO,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,SAAS,mBAAmB;AACvE;AAEO,SAAS,mBAAmB,SAGjC;AACA,QAAM,SAAS,QAAQ,MAAM;AAAA,IAC3B,CAAC,SAAS,KAAK,SAAS;AAAA,EAAA;AAE1B,MAAI,CAAC,UAAU,OAAO,OAAO,SAAS,YAAY,OAAO,SAAS;AAChE,WAAO,CAAA;AACT,QAAM,OAAO,OAAO;AACpB,SAAO;AAAA,IACL,GAAI,OAAO,KAAK,eAAe,WAC3B,EAAE,YAAY,KAAK,WAAA,IACnB,CAAA;AAAA,IACJ,GAAI,OAAO,KAAK,UAAU,WAAW,EAAE,OAAO,KAAK,UAAU,CAAA;AAAA,EAAC;AAElE;"}
|
|
@@ -15,6 +15,7 @@ function RuntimeBar({
|
|
|
15
15
|
models = [],
|
|
16
16
|
families = runtimeMode.SPEC_RUNTIME_FAMILIES,
|
|
17
17
|
reasoningEfforts = effortIcons.DEFAULT_REASONING_EFFORTS,
|
|
18
|
+
locked = false,
|
|
18
19
|
ariaLabel = "Runtime",
|
|
19
20
|
className
|
|
20
21
|
}) {
|
|
@@ -90,6 +91,7 @@ function RuntimeBar({
|
|
|
90
91
|
selectedModel: resolvedModel,
|
|
91
92
|
selectedModelUnavailable,
|
|
92
93
|
supportedEfforts,
|
|
94
|
+
locked,
|
|
93
95
|
ariaLabel,
|
|
94
96
|
className,
|
|
95
97
|
onFamilyChange: (familyId) => applyBackend(familyId, selection.mode),
|
|
@@ -114,6 +116,7 @@ function RuntimeBar({
|
|
|
114
116
|
selectedMode: selection.mode,
|
|
115
117
|
reasoningEfforts,
|
|
116
118
|
supportedEfforts,
|
|
119
|
+
locked,
|
|
117
120
|
ariaLabel,
|
|
118
121
|
className,
|
|
119
122
|
onBackendChange: applyBackend,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuntimeBar.cjs","sources":["../../../src/data/runtime/RuntimeBar.tsx"],"sourcesContent":["import { DEFAULT_REASONING_EFFORTS } from \"../chat/effort-icons\";\nimport type { ChatModel, ChatModelRuntime } from \"../chat/types\";\nimport { runtimeModelMatches } from \"./RuntimeBar.model\";\nimport { RuntimeBarCombo } from \"./RuntimeBarCombo\";\nimport { RuntimeBarSegments } from \"./RuntimeBarSegments\";\nimport { isSelectableModel } from \"./availability\";\nimport {\n effortOptionsForModel,\n reconcileModelCapabilities,\n} from \"./model-capabilities\";\nimport {\n SPEC_RUNTIME_FAMILIES,\n backendForFamilyMode,\n familyById,\n firstMode,\n modelBelongsToFamily,\n modelsForFamily,\n selectionForBackend,\n type SpecRuntimeFamily,\n} from \"./runtime-mode\";\n\nexport type RuntimeBarValue = ChatModelRuntime & {\n cliArgs?: Record<string, unknown>;\n};\n\nexport type RuntimeBarProps<T extends RuntimeBarValue = RuntimeBarValue> = {\n value: T;\n onChange: (value: T) => void;\n /** `segmented` uses field triggers; `combo` uses one direct-edit menu. */\n variant?: \"combo\" | \"segmented\";\n /** Model catalog. Only the selected family's models are listed; a family the\n * catalog does not describe is served by the segment's free-text entry. */\n models?: ChatModel[] | undefined;\n families?: SpecRuntimeFamily[] | undefined;\n /** Effort tiers offered when the catalog does not describe the model. */\n reasoningEfforts?: string[] | undefined;\n ariaLabel?: string | undefined;\n className?: string | undefined;\n};\n\n/** Self-describing runtime controls with segmented and combo presentations. */\nexport function RuntimeBar<T extends RuntimeBarValue>({\n value,\n onChange,\n variant = \"segmented\",\n models = [],\n families = SPEC_RUNTIME_FAMILIES,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n ariaLabel = \"Runtime\",\n className,\n}: RuntimeBarProps<T>) {\n const selection = selectionForBackend(families, value.backend);\n const family = familyById(families, selection.family);\n const mode =\n family.modes.find((entry) => entry.id === selection.mode) ??\n firstMode(family);\n const modelOptions = modelsForFamily(models, family, value.backend);\n const selectedModel = models.find(\n (entry) =>\n isSelectableModel(entry) &&\n entry.runtime?.backend === value.backend &&\n runtimeModelMatches(entry, value)
|
|
1
|
+
{"version":3,"file":"RuntimeBar.cjs","sources":["../../../src/data/runtime/RuntimeBar.tsx"],"sourcesContent":["import { DEFAULT_REASONING_EFFORTS } from \"../chat/effort-icons\";\nimport type { ChatModel, ChatModelRuntime } from \"../chat/types\";\nimport { runtimeModelMatches } from \"./RuntimeBar.model\";\nimport { RuntimeBarCombo } from \"./RuntimeBarCombo\";\nimport { RuntimeBarSegments } from \"./RuntimeBarSegments\";\nimport { isSelectableModel } from \"./availability\";\nimport {\n effortOptionsForModel,\n reconcileModelCapabilities,\n} from \"./model-capabilities\";\nimport {\n SPEC_RUNTIME_FAMILIES,\n backendForFamilyMode,\n familyById,\n firstMode,\n modelBelongsToFamily,\n modelsForFamily,\n selectionForBackend,\n type SpecRuntimeFamily,\n} from \"./runtime-mode\";\n\nexport type RuntimeBarValue = ChatModelRuntime & {\n cliArgs?: Record<string, unknown>;\n};\n\nexport type RuntimeBarProps<T extends RuntimeBarValue = RuntimeBarValue> = {\n value: T;\n onChange: (value: T) => void;\n /** `segmented` uses field triggers; `combo` uses one direct-edit menu. */\n variant?: \"combo\" | \"segmented\";\n /** Model catalog. Only the selected family's models are listed; a family the\n * catalog does not describe is served by the segment's free-text entry. */\n models?: ChatModel[] | undefined;\n families?: SpecRuntimeFamily[] | undefined;\n /** Effort tiers offered when the catalog does not describe the model. */\n reasoningEfforts?: string[] | undefined;\n /** Locks model identity (family, backend, and model) while leaving effort editable. */\n locked?: boolean | undefined;\n ariaLabel?: string | undefined;\n className?: string | undefined;\n};\n\n/** Self-describing runtime controls with segmented and combo presentations. */\nexport function RuntimeBar<T extends RuntimeBarValue>({\n value,\n onChange,\n variant = \"segmented\",\n models = [],\n families = SPEC_RUNTIME_FAMILIES,\n reasoningEfforts = DEFAULT_REASONING_EFFORTS,\n locked = false,\n ariaLabel = \"Runtime\",\n className,\n}: RuntimeBarProps<T>) {\n const selection = selectionForBackend(families, value.backend);\n const family = familyById(families, selection.family);\n const mode =\n family.modes.find((entry) => entry.id === selection.mode) ??\n firstMode(family);\n const modelOptions = modelsForFamily(models, family, value.backend);\n const selectedModel = models.find(\n (entry) =>\n isSelectableModel(entry) &&\n entry.runtime?.backend === value.backend &&\n runtimeModelMatches(entry, value),\n );\n const resolvedModel =\n selectedModel ??\n models.find(\n (entry) => isSelectableModel(entry) && runtimeModelMatches(entry, value),\n );\n const selectedModelUnavailable = Boolean(\n value.model &&\n models.some(\n (entry) => !isSelectableModel(entry) && runtimeModelMatches(entry, value),\n ),\n );\n const supportedEfforts = effortOptionsForModel(\n resolvedModel,\n reasoningEfforts,\n );\n\n const applyBackend = (familyId: string, modeId: string) => {\n const backend = backendForFamilyMode(families, familyId, modeId);\n if (backend === (value.backend ?? \"\")) return;\n let next = withOptionalRuntimeValue(\n withRuntimeValue(value, { backend }),\n \"cliArgs\",\n undefined,\n );\n if (\n !modelBelongsToFamily(\n value.model,\n models,\n familyById(families, familyId),\n backend,\n )\n ) {\n next = withoutCatalogModel(next);\n }\n onChange(next);\n };\n\n const applyCustomModel = (model: string) =>\n onChange(\n withOptionalRuntimeValue(withoutCatalogModel(value), \"model\", model),\n );\n // A menu row carries the backend of the catalog it was listed under, and one\n // catalog serves several backends — claude-cli and claude-cmux models are\n // listed as claude-agent rows. Copying the row's runtime verbatim would let\n // picking a model silently move the user off the mode they chose, so a row\n // drawn from the selected mode's own catalog keeps that mode.\n const applyModel = (model: ChatModel) => {\n const next = reconcileModelCapabilities(value, model, reasoningEfforts);\n if (!mode.provider || model.provider !== mode.provider) {\n onChange(next);\n return;\n }\n onChange(\n withOptionalRuntimeValue(\n withOptionalRuntimeValue(next, \"backend\", mode.backend),\n \"mode\",\n mode.id,\n ),\n );\n };\n const clearModel = () => onChange(withoutCatalogModel(value));\n const applyEffort = (effort: string) =>\n onChange(withOptionalRuntimeValue(value, \"effort\", effort));\n\n if (variant === \"combo\") {\n return (\n <RuntimeBarCombo\n value={value}\n families={families}\n family={family}\n mode={mode}\n selectedMode={selection.mode}\n models={modelOptions}\n selectedModel={resolvedModel}\n selectedModelUnavailable={selectedModelUnavailable}\n supportedEfforts={supportedEfforts}\n locked={locked}\n ariaLabel={ariaLabel}\n className={className}\n onFamilyChange={(familyId) => applyBackend(familyId, selection.mode)}\n onModeChange={(modeId) => applyBackend(family.id, modeId)}\n onModelSelect={applyModel}\n onModelClear={clearModel}\n onEffortChange={applyEffort}\n />\n );\n }\n\n return (\n <RuntimeBarSegments\n value={value}\n models={models}\n modelOptions={modelOptions}\n resolvedModel={resolvedModel}\n selectedModelUnavailable={selectedModelUnavailable}\n families={families}\n family={family}\n mode={mode}\n selectedMode={selection.mode}\n reasoningEfforts={reasoningEfforts}\n supportedEfforts={supportedEfforts}\n locked={locked}\n ariaLabel={ariaLabel}\n className={className}\n onBackendChange={applyBackend}\n onCustomModel={applyCustomModel}\n onModelSelect={applyModel}\n onModelClear={clearModel}\n onEffortChange={applyEffort}\n />\n );\n}\n\nfunction withoutCatalogModel<T extends RuntimeBarValue>(value: T): T {\n return withOptionalRuntimeValue(\n withOptionalRuntimeValue(value, \"model\", undefined),\n \"id\",\n undefined,\n );\n}\n\nfunction withRuntimeValue<T extends RuntimeBarValue>(\n value: T,\n patch: Partial<RuntimeBarValue>,\n): T {\n return { ...value, ...patch };\n}\n\nfunction withOptionalRuntimeValue<T extends RuntimeBarValue>(\n value: T,\n key: keyof RuntimeBarValue,\n next: unknown,\n): T {\n const updated = { ...value } as Record<string, unknown>;\n if (next === undefined || next === \"\") {\n delete updated[key];\n } else {\n updated[key] = next;\n }\n return updated as T;\n}\n"],"names":["SPEC_RUNTIME_FAMILIES","DEFAULT_REASONING_EFFORTS","selectionForBackend","familyById","firstMode","modelsForFamily","isSelectableModel","runtimeModelMatches","effortOptionsForModel","backendForFamilyMode","modelBelongsToFamily","reconcileModelCapabilities","jsx","RuntimeBarCombo","RuntimeBarSegments"],"mappings":"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"}
|
|
@@ -14,9 +14,11 @@ export type RuntimeBarProps<T extends RuntimeBarValue = RuntimeBarValue> = {
|
|
|
14
14
|
families?: SpecRuntimeFamily[] | undefined;
|
|
15
15
|
/** Effort tiers offered when the catalog does not describe the model. */
|
|
16
16
|
reasoningEfforts?: string[] | undefined;
|
|
17
|
+
/** Locks model identity (family, backend, and model) while leaving effort editable. */
|
|
18
|
+
locked?: boolean | undefined;
|
|
17
19
|
ariaLabel?: string | undefined;
|
|
18
20
|
className?: string | undefined;
|
|
19
21
|
};
|
|
20
22
|
/** Self-describing runtime controls with segmented and combo presentations. */
|
|
21
|
-
export declare function RuntimeBar<T extends RuntimeBarValue>({ value, onChange, variant, models, families, reasoningEfforts, ariaLabel, className, }: RuntimeBarProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
23
|
+
export declare function RuntimeBar<T extends RuntimeBarValue>({ value, onChange, variant, models, families, reasoningEfforts, locked, ariaLabel, className, }: RuntimeBarProps<T>): import("react/jsx-runtime").JSX.Element;
|
|
22
24
|
//# sourceMappingURL=RuntimeBar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RuntimeBar.d.ts","sourceRoot":"","sources":["../../../src/data/runtime/RuntimeBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AASjE,OAAO,EAQL,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI;IACzE,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAChC;gFAC4E;IAC5E,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC;IAC3C,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,CAAC,SAAS,eAAe,EAAE,EACpD,KAAK,EACL,QAAQ,EACR,OAAqB,EACrB,MAAW,EACX,QAAgC,EAChC,gBAA4C,EAC5C,SAAqB,EACrB,SAAS,GACV,EAAE,eAAe,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"RuntimeBar.d.ts","sourceRoot":"","sources":["../../../src/data/runtime/RuntimeBar.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AASjE,OAAO,EAQL,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG;IAC/C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI;IACzE,KAAK,EAAE,CAAC,CAAC;IACT,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IAC7B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAChC;gFAC4E;IAC5E,MAAM,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC;IAC3C,yEAAyE;IACzE,gBAAgB,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IACxC,uFAAuF;IACvF,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,CAAC,SAAS,eAAe,EAAE,EACpD,KAAK,EACL,QAAQ,EACR,OAAqB,EACrB,MAAW,EACX,QAAgC,EAChC,gBAA4C,EAC5C,MAAc,EACd,SAAqB,EACrB,SAAS,GACV,EAAE,eAAe,CAAC,CAAC,CAAC,2CA4HpB"}
|
|
@@ -13,6 +13,7 @@ function RuntimeBar({
|
|
|
13
13
|
models = [],
|
|
14
14
|
families = SPEC_RUNTIME_FAMILIES,
|
|
15
15
|
reasoningEfforts = DEFAULT_REASONING_EFFORTS,
|
|
16
|
+
locked = false,
|
|
16
17
|
ariaLabel = "Runtime",
|
|
17
18
|
className
|
|
18
19
|
}) {
|
|
@@ -88,6 +89,7 @@ function RuntimeBar({
|
|
|
88
89
|
selectedModel: resolvedModel,
|
|
89
90
|
selectedModelUnavailable,
|
|
90
91
|
supportedEfforts,
|
|
92
|
+
locked,
|
|
91
93
|
ariaLabel,
|
|
92
94
|
className,
|
|
93
95
|
onFamilyChange: (familyId) => applyBackend(familyId, selection.mode),
|
|
@@ -112,6 +114,7 @@ function RuntimeBar({
|
|
|
112
114
|
selectedMode: selection.mode,
|
|
113
115
|
reasoningEfforts,
|
|
114
116
|
supportedEfforts,
|
|
117
|
+
locked,
|
|
115
118
|
ariaLabel,
|
|
116
119
|
className,
|
|
117
120
|
onBackendChange: applyBackend,
|