@tangle-network/sandbox-ui 0.21.0 → 0.21.2
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/auth.js +3 -1
- package/dist/chat.d.ts +61 -1
- package/dist/chat.js +13 -3
- package/dist/{chunk-R6NONXFC.js → chunk-BP6ZTBI6.js} +2 -2
- package/dist/{chunk-IOB2PW5Z.js → chunk-R3ZMMCT3.js} +5 -13
- package/dist/chunk-SM7IJY4I.js +178 -0
- package/dist/{chunk-76IQLPW2.js → chunk-SOKKTB7W.js} +29 -5
- package/dist/{chunk-666PYT5K.js → chunk-TAAYDQGM.js} +101 -11
- package/dist/{chunk-7ZA5SEK3.js → chunk-UHFJXO24.js} +1 -177
- package/dist/{chunk-FLWMBK77.js → chunk-ZNYEJERQ.js} +403 -265
- package/dist/dashboard.d.ts +34 -1
- package/dist/dashboard.js +5 -2
- package/dist/globals.css +33 -198
- package/dist/hooks.d.ts +63 -2
- package/dist/hooks.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +23 -9
- package/dist/pages.js +134 -121
- package/dist/primitives.js +5 -3
- package/dist/styles.css +33 -198
- package/dist/workspace.js +1 -1
- package/package.json +12 -17
package/dist/auth.js
CHANGED
package/dist/chat.d.ts
CHANGED
|
@@ -28,6 +28,15 @@ interface AgentSessionHarnessControl {
|
|
|
28
28
|
/** Filter the selectable harnesses (e.g. by plan tier). Defaults to all. */
|
|
29
29
|
available?: ReadonlyArray<HarnessType>;
|
|
30
30
|
disabled?: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* A harness is bound to its chat session once the conversation has
|
|
33
|
+
* started. While locked the dropdown is inert and the model catalog
|
|
34
|
+
* is filtered to what this harness can run — fork the session to
|
|
35
|
+
* switch harness.
|
|
36
|
+
*/
|
|
37
|
+
locked?: boolean;
|
|
38
|
+
/** Tooltip shown on the locked trigger. */
|
|
39
|
+
lockReason?: string;
|
|
31
40
|
}
|
|
32
41
|
interface AgentSessionModelControl {
|
|
33
42
|
/** Canonical model id (provider-prefixed, e.g. "anthropic/claude-opus-4-8"). */
|
|
@@ -65,10 +74,61 @@ interface AgentSessionControlsProps {
|
|
|
65
74
|
* thinking-effort pickers in one row. Every section is optional and only
|
|
66
75
|
* renders when its control object is provided — never show a dead control.
|
|
67
76
|
*
|
|
77
|
+
* When BOTH harness and model controls are present the pair is kept
|
|
78
|
+
* coherent automatically (see harness-model-compat): picking a harness
|
|
79
|
+
* snaps an incompatible model to that harness's best catalog option;
|
|
80
|
+
* picking a model the current harness can't run switches to the model's
|
|
81
|
+
* native harness — unless the harness is `locked`, in which case the
|
|
82
|
+
* catalog itself is filtered to compatible models.
|
|
83
|
+
*
|
|
68
84
|
* Designed to slot into `SandboxWorkbench`'s `session.composerControls`.
|
|
69
85
|
*/
|
|
70
86
|
declare function AgentSessionControls({ harness, model, reasoning, trailing, className, }: AgentSessionControlsProps): react_jsx_runtime.JSX.Element | null;
|
|
71
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Harness ↔ model compatibility policy.
|
|
90
|
+
*
|
|
91
|
+
* Native CLI harnesses are vendor-locked: claude-code only drives
|
|
92
|
+
* Anthropic models, codex only OpenAI models. Router-backed harnesses
|
|
93
|
+
* (opencode) accept any catalog model. The pickers use this policy to
|
|
94
|
+
* keep the pair coherent: changing one side snaps the other to its
|
|
95
|
+
* nearest compatible choice instead of letting the user assemble a
|
|
96
|
+
* combination that fails at inference time.
|
|
97
|
+
*/
|
|
98
|
+
interface HarnessModelPolicy {
|
|
99
|
+
/** Canonical-id provider prefixes the harness can run; null = any. */
|
|
100
|
+
providers: ReadonlyArray<string> | null;
|
|
101
|
+
/**
|
|
102
|
+
* Patterns ranking snap targets, best first. Within one pattern's
|
|
103
|
+
* matches the highest version (numeric-aware descending sort) wins,
|
|
104
|
+
* so "latest standard frontier" stays correct as catalogs rotate.
|
|
105
|
+
*/
|
|
106
|
+
preferred: ReadonlyArray<RegExp>;
|
|
107
|
+
}
|
|
108
|
+
declare const HARNESS_MODEL_POLICIES: Record<HarnessType, HarnessModelPolicy>;
|
|
109
|
+
/** Provider prefix of a canonical id ("anthropic/claude-…" → "anthropic"). */
|
|
110
|
+
declare function modelProvider(modelId: string): string | null;
|
|
111
|
+
/**
|
|
112
|
+
* Ids without a provider prefix (consumer sentinels like "default",
|
|
113
|
+
* or an empty selection) are treated as compatible everywhere — they
|
|
114
|
+
* mean "the session's own configuration", which every harness honors.
|
|
115
|
+
*/
|
|
116
|
+
declare function isModelCompatibleWithHarness(harness: HarnessType, modelId: string): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Keeps `modelId` when the harness can run it; otherwise returns the
|
|
119
|
+
* harness's best compatible catalog model (preferred patterns in order,
|
|
120
|
+
* highest version within a pattern). When the catalog holds nothing
|
|
121
|
+
* compatible the original id is returned unchanged — the caller sees
|
|
122
|
+
* the incompatibility instead of a silent wrong substitution.
|
|
123
|
+
*/
|
|
124
|
+
declare function snapModelToHarness(harness: HarnessType, modelId: string, models: ReadonlyArray<ModelInfo>): string;
|
|
125
|
+
/**
|
|
126
|
+
* Keeps the harness when it can run `modelId`; otherwise returns the
|
|
127
|
+
* model's native harness (anthropic → claude-code, openai → codex),
|
|
128
|
+
* falling back to the router-backed opencode for everything else.
|
|
129
|
+
*/
|
|
130
|
+
declare function snapHarnessToModel(harness: HarnessType, modelId: string): HarnessType;
|
|
131
|
+
|
|
72
132
|
type ArtifactKind = string;
|
|
73
133
|
interface ArtifactScope {
|
|
74
134
|
kind: ArtifactKind;
|
|
@@ -166,4 +226,4 @@ declare function createFetchTransport(opts: {
|
|
|
166
226
|
fetchImpl?: typeof fetch;
|
|
167
227
|
}): ArtifactAgentDockTransport;
|
|
168
228
|
|
|
169
|
-
export { AgentSessionControls, type AgentSessionControlsProps, type AgentSessionHarnessControl, type AgentSessionModelControl, type AgentSessionReasoningControl, ArtifactAgentDock, type ArtifactAgentDockProps, type ArtifactAgentDockTransport, type ArtifactDockMessage, type ArtifactDockStreamEvent, type ArtifactKind, type ArtifactScope, DEFAULT_REASONING_LEVEL_OPTIONS, type ReasoningLevel, type ReasoningLevelOption, ReasoningLevelPicker, type ReasoningLevelPickerProps, createFetchTransport };
|
|
229
|
+
export { AgentSessionControls, type AgentSessionControlsProps, type AgentSessionHarnessControl, type AgentSessionModelControl, type AgentSessionReasoningControl, ArtifactAgentDock, type ArtifactAgentDockProps, type ArtifactAgentDockTransport, type ArtifactDockMessage, type ArtifactDockStreamEvent, type ArtifactKind, type ArtifactScope, DEFAULT_REASONING_LEVEL_OPTIONS, HARNESS_MODEL_POLICIES, type ReasoningLevel, type ReasoningLevelOption, ReasoningLevelPicker, type ReasoningLevelPickerProps, createFetchTransport, isModelCompatibleWithHarness, modelProvider, snapHarnessToModel, snapModelToHarness };
|
package/dist/chat.js
CHANGED
|
@@ -6,12 +6,17 @@ import {
|
|
|
6
6
|
ChatInput,
|
|
7
7
|
ChatMessage,
|
|
8
8
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
9
|
+
HARNESS_MODEL_POLICIES,
|
|
9
10
|
MessageList,
|
|
10
11
|
ReasoningLevelPicker,
|
|
11
12
|
ThinkingIndicator,
|
|
12
13
|
UserMessage,
|
|
13
|
-
createFetchTransport
|
|
14
|
-
|
|
14
|
+
createFetchTransport,
|
|
15
|
+
isModelCompatibleWithHarness,
|
|
16
|
+
modelProvider,
|
|
17
|
+
snapHarnessToModel,
|
|
18
|
+
snapModelToHarness
|
|
19
|
+
} from "./chunk-TAAYDQGM.js";
|
|
15
20
|
import "./chunk-ESRYVGHF.js";
|
|
16
21
|
import "./chunk-4KAPMTPU.js";
|
|
17
22
|
import "./chunk-EI44GEQ5.js";
|
|
@@ -23,9 +28,14 @@ export {
|
|
|
23
28
|
ChatInput,
|
|
24
29
|
ChatMessage,
|
|
25
30
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
31
|
+
HARNESS_MODEL_POLICIES,
|
|
26
32
|
MessageList,
|
|
27
33
|
ReasoningLevelPicker,
|
|
28
34
|
ThinkingIndicator,
|
|
29
35
|
UserMessage,
|
|
30
|
-
createFetchTransport
|
|
36
|
+
createFetchTransport,
|
|
37
|
+
isModelCompatibleWithHarness,
|
|
38
|
+
modelProvider,
|
|
39
|
+
snapHarnessToModel,
|
|
40
|
+
snapModelToHarness
|
|
31
41
|
};
|
|
@@ -1928,9 +1928,9 @@ function CalendarView({
|
|
|
1928
1928
|
children: date.getDate()
|
|
1929
1929
|
}
|
|
1930
1930
|
),
|
|
1931
|
-
/* @__PURE__ */ jsxs12("div", { className: "flex flex-
|
|
1931
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-0.5 w-full min-w-0", children: [
|
|
1932
1932
|
dayEvents.slice(0, 3).map(
|
|
1933
|
-
(evt) => renderEventChip ? /* @__PURE__ */ jsx12("
|
|
1933
|
+
(evt) => renderEventChip ? /* @__PURE__ */ jsx12("div", { className: "w-full min-w-0", children: renderEventChip(evt) }, evt.id) : /* @__PURE__ */ jsx12(
|
|
1934
1934
|
"div",
|
|
1935
1935
|
{
|
|
1936
1936
|
className: "w-full truncate text-[9px] px-1 py-0.5 rounded bg-primary/10 text-primary",
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TangleKnot
|
|
3
|
+
} from "./chunk-UHFJXO24.js";
|
|
4
|
+
|
|
1
5
|
// src/auth/index.ts
|
|
2
6
|
import {
|
|
3
7
|
AuthHeader,
|
|
@@ -10,18 +14,6 @@ import {
|
|
|
10
14
|
import { Button } from "@tangle-network/ui/primitives";
|
|
11
15
|
import { cn } from "@tangle-network/ui/utils";
|
|
12
16
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
-
function TangleMark({ className }) {
|
|
14
|
-
return /* @__PURE__ */ jsx(
|
|
15
|
-
"svg",
|
|
16
|
-
{
|
|
17
|
-
className,
|
|
18
|
-
viewBox: "0 0 24 24",
|
|
19
|
-
fill: "currentColor",
|
|
20
|
-
"aria-hidden": "true",
|
|
21
|
-
children: /* @__PURE__ */ jsx("path", { d: "M4 5h16v3.2h-6.4V19h-3.2V8.2H4V5z" })
|
|
22
|
-
}
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
17
|
function TangleLoginButton({
|
|
26
18
|
authUrl = "/auth/tangle",
|
|
27
19
|
variant = "default",
|
|
@@ -39,7 +31,7 @@ function TangleLoginButton({
|
|
|
39
31
|
},
|
|
40
32
|
...props,
|
|
41
33
|
children: [
|
|
42
|
-
/* @__PURE__ */ jsx(
|
|
34
|
+
/* @__PURE__ */ jsx(TangleKnot, { size: 20 }),
|
|
43
35
|
children ?? "Sign in with Tangle"
|
|
44
36
|
]
|
|
45
37
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
// src/primitives/index.ts
|
|
2
|
+
import {
|
|
3
|
+
Avatar,
|
|
4
|
+
AvatarFallback,
|
|
5
|
+
AvatarImage,
|
|
6
|
+
Badge,
|
|
7
|
+
badgeVariants,
|
|
8
|
+
Button,
|
|
9
|
+
buttonVariants,
|
|
10
|
+
Card,
|
|
11
|
+
CardContent,
|
|
12
|
+
CardDescription,
|
|
13
|
+
CardFooter,
|
|
14
|
+
CardHeader,
|
|
15
|
+
CardTitle,
|
|
16
|
+
CodeBlock,
|
|
17
|
+
CopyButton,
|
|
18
|
+
Dialog,
|
|
19
|
+
DialogClose,
|
|
20
|
+
DialogContent,
|
|
21
|
+
DialogDescription,
|
|
22
|
+
DialogFooter,
|
|
23
|
+
DialogHeader,
|
|
24
|
+
DialogOverlay,
|
|
25
|
+
DialogPortal,
|
|
26
|
+
DialogTitle,
|
|
27
|
+
DialogTrigger,
|
|
28
|
+
DropZone,
|
|
29
|
+
DropdownMenu,
|
|
30
|
+
DropdownMenuCheckboxItem,
|
|
31
|
+
DropdownMenuContent,
|
|
32
|
+
DropdownMenuGroup,
|
|
33
|
+
DropdownMenuItem,
|
|
34
|
+
DropdownMenuLabel,
|
|
35
|
+
DropdownMenuPortal,
|
|
36
|
+
DropdownMenuRadioGroup,
|
|
37
|
+
DropdownMenuRadioItem,
|
|
38
|
+
DropdownMenuSeparator,
|
|
39
|
+
DropdownMenuShortcut,
|
|
40
|
+
DropdownMenuSub,
|
|
41
|
+
DropdownMenuSubContent,
|
|
42
|
+
DropdownMenuSubTrigger,
|
|
43
|
+
DropdownMenuTrigger,
|
|
44
|
+
EmptyState,
|
|
45
|
+
InlineCode,
|
|
46
|
+
Input,
|
|
47
|
+
Label,
|
|
48
|
+
Progress,
|
|
49
|
+
SegmentedControl,
|
|
50
|
+
Select,
|
|
51
|
+
SelectContent,
|
|
52
|
+
SelectGroup,
|
|
53
|
+
SelectItem,
|
|
54
|
+
SelectLabel,
|
|
55
|
+
SelectScrollDownButton,
|
|
56
|
+
SelectScrollUpButton,
|
|
57
|
+
SelectSeparator,
|
|
58
|
+
SelectTrigger,
|
|
59
|
+
SelectValue,
|
|
60
|
+
SidebarDropZone,
|
|
61
|
+
Skeleton,
|
|
62
|
+
SkeletonCard,
|
|
63
|
+
SkeletonTable,
|
|
64
|
+
StatCard,
|
|
65
|
+
Switch,
|
|
66
|
+
Table,
|
|
67
|
+
TableBody,
|
|
68
|
+
TableCaption,
|
|
69
|
+
TableCell,
|
|
70
|
+
TableFooter,
|
|
71
|
+
TableHead,
|
|
72
|
+
TableHeader,
|
|
73
|
+
TableRow,
|
|
74
|
+
Tabs,
|
|
75
|
+
TabsContent,
|
|
76
|
+
TabsList,
|
|
77
|
+
TabsTrigger,
|
|
78
|
+
TerminalCursor,
|
|
79
|
+
TerminalDisplay,
|
|
80
|
+
TerminalInput,
|
|
81
|
+
TerminalLine,
|
|
82
|
+
Textarea,
|
|
83
|
+
ThemeToggle,
|
|
84
|
+
ToastContainer,
|
|
85
|
+
ToastProvider,
|
|
86
|
+
UploadProgress,
|
|
87
|
+
useTheme,
|
|
88
|
+
useToast
|
|
89
|
+
} from "@tangle-network/ui/primitives";
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
Avatar,
|
|
93
|
+
AvatarFallback,
|
|
94
|
+
AvatarImage,
|
|
95
|
+
Badge,
|
|
96
|
+
badgeVariants,
|
|
97
|
+
Button,
|
|
98
|
+
buttonVariants,
|
|
99
|
+
Card,
|
|
100
|
+
CardContent,
|
|
101
|
+
CardDescription,
|
|
102
|
+
CardFooter,
|
|
103
|
+
CardHeader,
|
|
104
|
+
CardTitle,
|
|
105
|
+
CodeBlock,
|
|
106
|
+
CopyButton,
|
|
107
|
+
Dialog,
|
|
108
|
+
DialogClose,
|
|
109
|
+
DialogContent,
|
|
110
|
+
DialogDescription,
|
|
111
|
+
DialogFooter,
|
|
112
|
+
DialogHeader,
|
|
113
|
+
DialogOverlay,
|
|
114
|
+
DialogPortal,
|
|
115
|
+
DialogTitle,
|
|
116
|
+
DialogTrigger,
|
|
117
|
+
DropZone,
|
|
118
|
+
DropdownMenu,
|
|
119
|
+
DropdownMenuCheckboxItem,
|
|
120
|
+
DropdownMenuContent,
|
|
121
|
+
DropdownMenuGroup,
|
|
122
|
+
DropdownMenuItem,
|
|
123
|
+
DropdownMenuLabel,
|
|
124
|
+
DropdownMenuPortal,
|
|
125
|
+
DropdownMenuRadioGroup,
|
|
126
|
+
DropdownMenuRadioItem,
|
|
127
|
+
DropdownMenuSeparator,
|
|
128
|
+
DropdownMenuShortcut,
|
|
129
|
+
DropdownMenuSub,
|
|
130
|
+
DropdownMenuSubContent,
|
|
131
|
+
DropdownMenuSubTrigger,
|
|
132
|
+
DropdownMenuTrigger,
|
|
133
|
+
EmptyState,
|
|
134
|
+
InlineCode,
|
|
135
|
+
Input,
|
|
136
|
+
Label,
|
|
137
|
+
Progress,
|
|
138
|
+
SegmentedControl,
|
|
139
|
+
Select,
|
|
140
|
+
SelectContent,
|
|
141
|
+
SelectGroup,
|
|
142
|
+
SelectItem,
|
|
143
|
+
SelectLabel,
|
|
144
|
+
SelectScrollDownButton,
|
|
145
|
+
SelectScrollUpButton,
|
|
146
|
+
SelectSeparator,
|
|
147
|
+
SelectTrigger,
|
|
148
|
+
SelectValue,
|
|
149
|
+
SidebarDropZone,
|
|
150
|
+
Skeleton,
|
|
151
|
+
SkeletonCard,
|
|
152
|
+
SkeletonTable,
|
|
153
|
+
StatCard,
|
|
154
|
+
Switch,
|
|
155
|
+
Table,
|
|
156
|
+
TableBody,
|
|
157
|
+
TableCaption,
|
|
158
|
+
TableCell,
|
|
159
|
+
TableFooter,
|
|
160
|
+
TableHead,
|
|
161
|
+
TableHeader,
|
|
162
|
+
TableRow,
|
|
163
|
+
Tabs,
|
|
164
|
+
TabsContent,
|
|
165
|
+
TabsList,
|
|
166
|
+
TabsTrigger,
|
|
167
|
+
TerminalCursor,
|
|
168
|
+
TerminalDisplay,
|
|
169
|
+
TerminalInput,
|
|
170
|
+
TerminalLine,
|
|
171
|
+
Textarea,
|
|
172
|
+
ThemeToggle,
|
|
173
|
+
ToastContainer,
|
|
174
|
+
ToastProvider,
|
|
175
|
+
UploadProgress,
|
|
176
|
+
useTheme,
|
|
177
|
+
useToast
|
|
178
|
+
};
|
|
@@ -22,9 +22,13 @@ function useSandboxMetrics({
|
|
|
22
22
|
sandboxId,
|
|
23
23
|
token,
|
|
24
24
|
enabled = true,
|
|
25
|
-
intervalMs = 3e3
|
|
25
|
+
intervalMs = 3e3,
|
|
26
|
+
historyLimit = 120
|
|
26
27
|
}) {
|
|
27
28
|
const [metrics, setMetrics] = React.useState(null);
|
|
29
|
+
const [system, setSystem] = React.useState(null);
|
|
30
|
+
const [latency, setLatency] = React.useState(null);
|
|
31
|
+
const [history, setHistory] = React.useState([]);
|
|
28
32
|
const [loading, setLoading] = React.useState(false);
|
|
29
33
|
const [error, setError] = React.useState(null);
|
|
30
34
|
const [lastUpdatedAt, setLastUpdatedAt] = React.useState(null);
|
|
@@ -38,6 +42,9 @@ function useSandboxMetrics({
|
|
|
38
42
|
sampleRef.current = null;
|
|
39
43
|
hasLoadedRef.current = false;
|
|
40
44
|
setMetrics(null);
|
|
45
|
+
setSystem(null);
|
|
46
|
+
setLatency(null);
|
|
47
|
+
setHistory([]);
|
|
41
48
|
setLastUpdatedAt(null);
|
|
42
49
|
setError(null);
|
|
43
50
|
if (sandboxCleared) setLoading(false);
|
|
@@ -69,8 +76,8 @@ function useSandboxMetrics({
|
|
|
69
76
|
}
|
|
70
77
|
const data = await res.json();
|
|
71
78
|
const user = data?.process?.cpuSeconds?.user ?? 0;
|
|
72
|
-
const
|
|
73
|
-
const cpuSeconds = user +
|
|
79
|
+
const system2 = data?.process?.cpuSeconds?.system ?? 0;
|
|
80
|
+
const cpuSeconds = user + system2;
|
|
74
81
|
const wallMs = Date.now();
|
|
75
82
|
if (cancelled) return;
|
|
76
83
|
let cpuPercent = null;
|
|
@@ -89,6 +96,23 @@ function useSandboxMetrics({
|
|
|
89
96
|
heapUsedBytes: data?.process?.memoryBytes?.heapUsed ?? 0,
|
|
90
97
|
heapTotalBytes: data?.process?.memoryBytes?.heapTotal ?? 0
|
|
91
98
|
});
|
|
99
|
+
const sys = data?.system ?? null;
|
|
100
|
+
setSystem(sys);
|
|
101
|
+
setLatency(data?.latency ?? null);
|
|
102
|
+
if (sys) {
|
|
103
|
+
const sample = {
|
|
104
|
+
at: wallMs,
|
|
105
|
+
cpuPercent: sys.cpuPercent,
|
|
106
|
+
memoryUsedBytes: sys.memory?.usedBytes ?? null,
|
|
107
|
+
memoryTotalBytes: sys.memory?.totalBytes ?? null,
|
|
108
|
+
diskUsedBytes: sys.disk?.usedBytes ?? null,
|
|
109
|
+
diskTotalBytes: sys.disk?.totalBytes ?? null
|
|
110
|
+
};
|
|
111
|
+
setHistory((prevHistory) => {
|
|
112
|
+
const next = [...prevHistory, sample];
|
|
113
|
+
return next.length > historyLimit ? next.slice(next.length - historyLimit) : next;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
92
116
|
setLastUpdatedAt(wallMs);
|
|
93
117
|
setError(null);
|
|
94
118
|
hasLoadedRef.current = true;
|
|
@@ -113,8 +137,8 @@ function useSandboxMetrics({
|
|
|
113
137
|
controller.abort();
|
|
114
138
|
if (timeoutId !== null) window.clearTimeout(timeoutId);
|
|
115
139
|
};
|
|
116
|
-
}, [apiBaseUrl, sandboxId, token, enabled, intervalMs]);
|
|
117
|
-
return { metrics, loading, error, lastUpdatedAt };
|
|
140
|
+
}, [apiBaseUrl, sandboxId, token, enabled, intervalMs, historyLimit]);
|
|
141
|
+
return { metrics, system, latency, history, loading, error, lastUpdatedAt };
|
|
118
142
|
}
|
|
119
143
|
|
|
120
144
|
// src/hooks/use-session-crud.ts
|
|
@@ -2,7 +2,8 @@ import {
|
|
|
2
2
|
HARNESS_OPTIONS
|
|
3
3
|
} from "./chunk-ESRYVGHF.js";
|
|
4
4
|
import {
|
|
5
|
-
ModelPicker
|
|
5
|
+
ModelPicker,
|
|
6
|
+
canonicalModelId
|
|
6
7
|
} from "./chunk-4KAPMTPU.js";
|
|
7
8
|
import {
|
|
8
9
|
cn
|
|
@@ -100,13 +101,76 @@ function ReasoningLevelPicker({
|
|
|
100
101
|
|
|
101
102
|
// src/chat/agent-session-controls.tsx
|
|
102
103
|
import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
|
|
103
|
-
import { Bot, ChevronDown as ChevronDown2 } from "lucide-react";
|
|
104
|
+
import { Bot, ChevronDown as ChevronDown2, Lock } from "lucide-react";
|
|
105
|
+
|
|
106
|
+
// src/chat/harness-model-compat.ts
|
|
107
|
+
var HARNESS_MODEL_POLICIES = {
|
|
108
|
+
opencode: { providers: null, preferred: [] },
|
|
109
|
+
"claude-code": {
|
|
110
|
+
providers: ["anthropic"],
|
|
111
|
+
preferred: [
|
|
112
|
+
/^anthropic\/claude-opus-[\d.-]+$/,
|
|
113
|
+
/^anthropic\/claude-sonnet-[\d.-]+$/,
|
|
114
|
+
/^anthropic\//
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
codex: {
|
|
118
|
+
providers: ["openai"],
|
|
119
|
+
// Standard frontier tier first (gpt-N / gpt-N.N), then any gpt
|
|
120
|
+
// variant (mini/nano), then anything OpenAI.
|
|
121
|
+
preferred: [/^openai\/gpt-\d+(\.\d+)?$/, /^openai\/gpt/, /^openai\//]
|
|
122
|
+
},
|
|
123
|
+
amp: { providers: null, preferred: [] },
|
|
124
|
+
"factory-droids": { providers: null, preferred: [] },
|
|
125
|
+
"cli-base": { providers: null, preferred: [] }
|
|
126
|
+
};
|
|
127
|
+
var PROVIDER_PREFERRED_HARNESS = {
|
|
128
|
+
anthropic: "claude-code",
|
|
129
|
+
openai: "codex"
|
|
130
|
+
};
|
|
131
|
+
function modelProvider(modelId) {
|
|
132
|
+
const slash = modelId.indexOf("/");
|
|
133
|
+
return slash > 0 ? modelId.slice(0, slash) : null;
|
|
134
|
+
}
|
|
135
|
+
function isModelCompatibleWithHarness(harness, modelId) {
|
|
136
|
+
const policy = HARNESS_MODEL_POLICIES[harness];
|
|
137
|
+
if (!policy || policy.providers === null) return true;
|
|
138
|
+
const provider = modelProvider(modelId);
|
|
139
|
+
if (!provider) return true;
|
|
140
|
+
return policy.providers.includes(provider);
|
|
141
|
+
}
|
|
142
|
+
var numericDesc = new Intl.Collator(void 0, {
|
|
143
|
+
numeric: true,
|
|
144
|
+
sensitivity: "base"
|
|
145
|
+
});
|
|
146
|
+
function snapModelToHarness(harness, modelId, models) {
|
|
147
|
+
if (isModelCompatibleWithHarness(harness, modelId)) return modelId;
|
|
148
|
+
const ids = models.map(canonicalModelId);
|
|
149
|
+
const policy = HARNESS_MODEL_POLICIES[harness];
|
|
150
|
+
for (const pattern of policy.preferred) {
|
|
151
|
+
const matches = ids.filter((id) => pattern.test(id)).sort((a, b) => numericDesc.compare(b, a));
|
|
152
|
+
if (matches.length > 0) return matches[0];
|
|
153
|
+
}
|
|
154
|
+
const fallback = ids.find(
|
|
155
|
+
(id) => isModelCompatibleWithHarness(harness, id)
|
|
156
|
+
);
|
|
157
|
+
return fallback ?? modelId;
|
|
158
|
+
}
|
|
159
|
+
function snapHarnessToModel(harness, modelId) {
|
|
160
|
+
if (isModelCompatibleWithHarness(harness, modelId)) return harness;
|
|
161
|
+
const provider = modelProvider(modelId);
|
|
162
|
+
return provider && PROVIDER_PREFERRED_HARNESS[provider] || "opencode";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/chat/agent-session-controls.tsx
|
|
104
166
|
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
105
167
|
function HarnessDropdown({
|
|
106
168
|
value,
|
|
107
169
|
onChange,
|
|
108
170
|
available,
|
|
109
|
-
disabled
|
|
171
|
+
disabled,
|
|
172
|
+
locked,
|
|
173
|
+
lockReason
|
|
110
174
|
}) {
|
|
111
175
|
const allowed = new Set(
|
|
112
176
|
available ?? HARNESS_OPTIONS.map((h) => h.type)
|
|
@@ -118,19 +182,20 @@ function HarnessDropdown({
|
|
|
118
182
|
"button",
|
|
119
183
|
{
|
|
120
184
|
type: "button",
|
|
121
|
-
disabled,
|
|
185
|
+
disabled: disabled || locked,
|
|
186
|
+
title: locked ? lockReason : void 0,
|
|
122
187
|
className: cn(
|
|
123
188
|
"inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-card px-2.5",
|
|
124
189
|
"text-xs font-medium text-foreground shadow-sm transition-colors",
|
|
125
190
|
"hover:border-primary/30 hover:bg-accent/30 focus:outline-none focus:border-primary/40",
|
|
126
191
|
"data-[state=open]:border-primary/40 data-[state=open]:bg-accent/30",
|
|
127
|
-
"disabled:cursor-not-allowed disabled:opacity-
|
|
192
|
+
"disabled:cursor-not-allowed disabled:opacity-60"
|
|
128
193
|
),
|
|
129
194
|
"aria-label": "Agent harness",
|
|
130
195
|
children: [
|
|
131
|
-
/* @__PURE__ */ jsx2(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
196
|
+
locked ? /* @__PURE__ */ jsx2(Lock, { className: "h-3 w-3 text-muted-foreground" }) : /* @__PURE__ */ jsx2(Bot, { className: "h-3.5 w-3.5 text-muted-foreground" }),
|
|
132
197
|
/* @__PURE__ */ jsx2("span", { children: selected?.label ?? value }),
|
|
133
|
-
/* @__PURE__ */ jsx2(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
198
|
+
!locked && /* @__PURE__ */ jsx2(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
134
199
|
]
|
|
135
200
|
}
|
|
136
201
|
) }),
|
|
@@ -177,25 +242,45 @@ function AgentSessionControls({
|
|
|
177
242
|
className
|
|
178
243
|
}) {
|
|
179
244
|
if (!harness && !model && !reasoning && !trailing) return null;
|
|
245
|
+
const handleHarnessChange = (next) => {
|
|
246
|
+
harness?.onChange(next);
|
|
247
|
+
if (model) {
|
|
248
|
+
const snapped = snapModelToHarness(next, model.value, model.models);
|
|
249
|
+
if (snapped !== model.value) model.onChange(snapped);
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const handleModelChange = (nextModelId) => {
|
|
253
|
+
model?.onChange(nextModelId);
|
|
254
|
+
if (harness && !harness.locked) {
|
|
255
|
+
const snapped = snapHarnessToModel(harness.value, nextModelId);
|
|
256
|
+
if (snapped !== harness.value) harness.onChange(snapped);
|
|
257
|
+
}
|
|
258
|
+
};
|
|
259
|
+
const visibleModels = model && harness?.locked ? model.models.filter(
|
|
260
|
+
(entry) => isModelCompatibleWithHarness(
|
|
261
|
+
harness.value,
|
|
262
|
+
canonicalModelId(entry)
|
|
263
|
+
)
|
|
264
|
+
) : model?.models;
|
|
180
265
|
return /* @__PURE__ */ jsxs2(
|
|
181
266
|
"div",
|
|
182
267
|
{
|
|
183
268
|
className: cn("flex flex-wrap items-center gap-2", className),
|
|
184
269
|
"data-testid": "agent-session-controls",
|
|
185
270
|
children: [
|
|
186
|
-
harness && /* @__PURE__ */ jsx2(HarnessDropdown, { ...harness }),
|
|
271
|
+
harness && /* @__PURE__ */ jsx2(HarnessDropdown, { ...harness, onChange: handleHarnessChange }),
|
|
187
272
|
model && /* @__PURE__ */ jsx2(
|
|
188
273
|
ModelPicker,
|
|
189
274
|
{
|
|
190
275
|
variant: "pill",
|
|
191
276
|
label: "",
|
|
192
277
|
value: model.value,
|
|
193
|
-
onChange:
|
|
194
|
-
models:
|
|
278
|
+
onChange: handleModelChange,
|
|
279
|
+
models: visibleModels ?? [],
|
|
195
280
|
loading: model.loading,
|
|
196
281
|
popular: model.popular,
|
|
197
282
|
recents: model.recents,
|
|
198
|
-
disabled: model.disabled ||
|
|
283
|
+
disabled: model.disabled || (visibleModels ?? []).length === 0
|
|
199
284
|
}
|
|
200
285
|
),
|
|
201
286
|
reasoning && /* @__PURE__ */ jsx2(
|
|
@@ -554,6 +639,11 @@ function createFetchTransport(opts) {
|
|
|
554
639
|
export {
|
|
555
640
|
DEFAULT_REASONING_LEVEL_OPTIONS,
|
|
556
641
|
ReasoningLevelPicker,
|
|
642
|
+
HARNESS_MODEL_POLICIES,
|
|
643
|
+
modelProvider,
|
|
644
|
+
isModelCompatibleWithHarness,
|
|
645
|
+
snapModelToHarness,
|
|
646
|
+
snapHarnessToModel,
|
|
557
647
|
AgentSessionControls,
|
|
558
648
|
ArtifactAgentDock,
|
|
559
649
|
createFetchTransport,
|