@tangle-network/sandbox-ui 0.20.3 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -228,13 +228,19 @@ function useSessionStream({
228
228
  }
229
229
  }, [refetch]);
230
230
  handleSSEEventRef.current = handleSSEEvent;
231
- const send = useCallback(async (text) => {
231
+ const send = useCallback(async (text, options) => {
232
232
  if (!token || !sessionId || !apiUrl) return;
233
233
  try {
234
234
  const url = `${apiUrl}/session/sessions/${encodeURIComponent(sessionId)}/messages`;
235
235
  await fetchJson(url, token, {
236
236
  method: "POST",
237
- body: JSON.stringify({ parts: [{ type: "text", text }] })
237
+ body: JSON.stringify({
238
+ parts: [{ type: "text", text }],
239
+ ...options?.agent ? { agent: options.agent } : {},
240
+ ...options?.model ? { model: options.model } : {},
241
+ ...options?.system ? { system: options.system } : {},
242
+ ...options?.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}
243
+ })
238
244
  });
239
245
  setIsStreaming(true);
240
246
  } catch (err) {
@@ -1474,6 +1474,7 @@ function SandboxWorkbench({
1474
1474
  ] })
1475
1475
  ] })
1476
1476
  ] });
1477
+ const { composerControls, ...chatSession } = session;
1477
1478
  const center = /* @__PURE__ */ jsx9(
1478
1479
  ArtifactPane2,
1479
1480
  {
@@ -1484,14 +1485,17 @@ function SandboxWorkbench({
1484
1485
  headerActions: session.headerActions,
1485
1486
  className: "h-full",
1486
1487
  contentClassName: "bg-background",
1487
- children: /* @__PURE__ */ jsx9(
1488
- ChatContainer,
1489
- {
1490
- ...session,
1491
- className: "h-full",
1492
- presentation: session.presentation ?? "timeline"
1493
- }
1494
- )
1488
+ children: /* @__PURE__ */ jsxs9("div", { className: "flex h-full min-h-0 flex-col", children: [
1489
+ /* @__PURE__ */ jsx9(
1490
+ ChatContainer,
1491
+ {
1492
+ ...chatSession,
1493
+ className: "min-h-0 flex-1",
1494
+ presentation: session.presentation ?? "timeline"
1495
+ }
1496
+ ),
1497
+ composerControls && /* @__PURE__ */ jsx9("div", { className: "shrink-0 border-t border-border bg-muted/30 px-3 py-2", children: composerControls })
1498
+ ] })
1495
1499
  }
1496
1500
  );
1497
1501
  const artifactPanel = artifacts.length > 0 ? /* @__PURE__ */ jsxs9("section", { className: "flex h-full min-h-0 flex-col bg-background", children: [
@@ -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 system = data?.process?.cpuSeconds?.system ?? 0;
73
- const cpuSeconds = user + system;
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
@@ -1,3 +1,10 @@
1
+ import {
2
+ HARNESS_OPTIONS
3
+ } from "./chunk-ESRYVGHF.js";
4
+ import {
5
+ ModelPicker,
6
+ canonicalModelId
7
+ } from "./chunk-4KAPMTPU.js";
1
8
  import {
2
9
  cn
3
10
  } from "./chunk-EI44GEQ5.js";
@@ -92,11 +99,210 @@ function ReasoningLevelPicker({
92
99
  ] });
93
100
  }
94
101
 
102
+ // src/chat/agent-session-controls.tsx
103
+ import * as DropdownMenu2 from "@radix-ui/react-dropdown-menu";
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
166
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
167
+ function HarnessDropdown({
168
+ value,
169
+ onChange,
170
+ available,
171
+ disabled,
172
+ locked,
173
+ lockReason
174
+ }) {
175
+ const allowed = new Set(
176
+ available ?? HARNESS_OPTIONS.map((h) => h.type)
177
+ );
178
+ const options = HARNESS_OPTIONS.filter((h) => allowed.has(h.type));
179
+ const selected = options.find((option) => option.type === value);
180
+ return /* @__PURE__ */ jsxs2(DropdownMenu2.Root, { children: [
181
+ /* @__PURE__ */ jsx2(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs2(
182
+ "button",
183
+ {
184
+ type: "button",
185
+ disabled: disabled || locked,
186
+ title: locked ? lockReason : void 0,
187
+ className: cn(
188
+ "inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-card px-2.5",
189
+ "text-xs font-medium text-foreground shadow-sm transition-colors",
190
+ "hover:border-primary/30 hover:bg-accent/30 focus:outline-none focus:border-primary/40",
191
+ "data-[state=open]:border-primary/40 data-[state=open]:bg-accent/30",
192
+ "disabled:cursor-not-allowed disabled:opacity-60"
193
+ ),
194
+ "aria-label": "Agent harness",
195
+ children: [
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" }),
197
+ /* @__PURE__ */ jsx2("span", { children: selected?.label ?? value }),
198
+ !locked && /* @__PURE__ */ jsx2(ChevronDown2, { className: "h-3.5 w-3.5 text-muted-foreground" })
199
+ ]
200
+ }
201
+ ) }),
202
+ /* @__PURE__ */ jsx2(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsx2(
203
+ DropdownMenu2.Content,
204
+ {
205
+ align: "start",
206
+ sideOffset: 6,
207
+ className: cn(
208
+ "z-50 w-72 overflow-hidden rounded-[var(--radius-md)] border border-border bg-card p-1",
209
+ "shadow-[var(--shadow-dropdown)]",
210
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
211
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
212
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95"
213
+ ),
214
+ children: options.map((option) => /* @__PURE__ */ jsxs2(
215
+ DropdownMenu2.Item,
216
+ {
217
+ onSelect: (event) => {
218
+ event.preventDefault();
219
+ onChange(option.type);
220
+ },
221
+ className: cn(
222
+ "flex cursor-pointer flex-col gap-0.5 rounded-md px-2.5 py-2 outline-none",
223
+ "transition-colors hover:bg-accent/40 focus:bg-accent/40",
224
+ option.type === value && "bg-[var(--accent-surface-soft)] text-[var(--accent-text)]"
225
+ ),
226
+ children: [
227
+ /* @__PURE__ */ jsx2("span", { className: "text-sm font-medium", children: option.label }),
228
+ option.description && /* @__PURE__ */ jsx2("span", { className: "text-xs text-muted-foreground", children: option.description })
229
+ ]
230
+ },
231
+ option.type
232
+ ))
233
+ }
234
+ ) })
235
+ ] });
236
+ }
237
+ function AgentSessionControls({
238
+ harness,
239
+ model,
240
+ reasoning,
241
+ trailing,
242
+ className
243
+ }) {
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;
265
+ return /* @__PURE__ */ jsxs2(
266
+ "div",
267
+ {
268
+ className: cn("flex flex-wrap items-center gap-2", className),
269
+ "data-testid": "agent-session-controls",
270
+ children: [
271
+ harness && /* @__PURE__ */ jsx2(HarnessDropdown, { ...harness, onChange: handleHarnessChange }),
272
+ model && /* @__PURE__ */ jsx2(
273
+ ModelPicker,
274
+ {
275
+ variant: "pill",
276
+ label: "",
277
+ value: model.value,
278
+ onChange: handleModelChange,
279
+ models: visibleModels ?? [],
280
+ loading: model.loading,
281
+ popular: model.popular,
282
+ recents: model.recents,
283
+ disabled: model.disabled || (visibleModels ?? []).length === 0
284
+ }
285
+ ),
286
+ reasoning && /* @__PURE__ */ jsx2(
287
+ ReasoningLevelPicker,
288
+ {
289
+ value: reasoning.value,
290
+ onChange: reasoning.onChange,
291
+ options: reasoning.options,
292
+ disabled: reasoning.disabled
293
+ }
294
+ ),
295
+ trailing && /* @__PURE__ */ jsx2("div", { className: "ml-auto flex items-center gap-2", children: trailing })
296
+ ]
297
+ }
298
+ );
299
+ }
300
+
95
301
  // src/chat/artifact-agent-dock.tsx
96
302
  import * as React from "react";
97
303
  import { Sparkles, Send, Square, X } from "lucide-react";
98
304
  import { ChatMessage } from "@tangle-network/ui/chat";
99
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
305
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
100
306
  function defaultScopeLabel(scope) {
101
307
  if (scope.label) return scope.label;
102
308
  switch (scope.kind) {
@@ -262,7 +468,7 @@ function ArtifactAgentDock({
262
468
  }, [threadId, transport]);
263
469
  if (!open) return null;
264
470
  const heading = defaultScopeLabel(scope);
265
- return /* @__PURE__ */ jsxs2(
471
+ return /* @__PURE__ */ jsxs3(
266
472
  "aside",
267
473
  {
268
474
  className: cn(
@@ -271,38 +477,38 @@ function ArtifactAgentDock({
271
477
  ),
272
478
  "aria-label": `Agent chat about ${heading}`,
273
479
  children: [
274
- /* @__PURE__ */ jsxs2("header", { className: "flex items-center justify-between border-b border-border bg-background/60 px-4 py-3", children: [
275
- /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-center gap-2", children: [
276
- /* @__PURE__ */ jsx2(Sparkles, { className: "h-4 w-4 shrink-0 text-primary" }),
277
- /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
278
- /* @__PURE__ */ jsx2("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
279
- /* @__PURE__ */ jsx2("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
480
+ /* @__PURE__ */ jsxs3("header", { className: "flex items-center justify-between border-b border-border bg-background/60 px-4 py-3", children: [
481
+ /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-2", children: [
482
+ /* @__PURE__ */ jsx3(Sparkles, { className: "h-4 w-4 shrink-0 text-primary" }),
483
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0", children: [
484
+ /* @__PURE__ */ jsx3("div", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: "Discussing" }),
485
+ /* @__PURE__ */ jsx3("div", { className: "truncate text-sm font-semibold text-foreground", children: heading })
280
486
  ] })
281
487
  ] }),
282
- /* @__PURE__ */ jsx2(
488
+ /* @__PURE__ */ jsx3(
283
489
  "button",
284
490
  {
285
491
  type: "button",
286
492
  onClick: () => onOpenChange(false),
287
493
  "aria-label": "Close artifact chat",
288
494
  className: "rounded-md p-1 text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
289
- children: /* @__PURE__ */ jsx2(X, { className: "h-4 w-4" })
495
+ children: /* @__PURE__ */ jsx3(X, { className: "h-4 w-4" })
290
496
  }
291
497
  )
292
498
  ] }),
293
- /* @__PURE__ */ jsx2("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx2("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs2("div", { className: "px-3 py-12 text-center", children: [
294
- /* @__PURE__ */ jsx2(Sparkles, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
295
- /* @__PURE__ */ jsxs2("p", { className: "text-sm text-foreground", children: [
499
+ /* @__PURE__ */ jsx3("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-3 py-4", children: loading ? /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "Loading conversation\u2026" }) : messages.length === 0 && !streamContent ? /* @__PURE__ */ jsxs3("div", { className: "px-3 py-12 text-center", children: [
500
+ /* @__PURE__ */ jsx3(Sparkles, { className: "mx-auto mb-3 h-5 w-5 text-muted-foreground/40" }),
501
+ /* @__PURE__ */ jsxs3("p", { className: "text-sm text-foreground", children: [
296
502
  "Ask the agent about this ",
297
503
  scope.kind === "vault-file" ? "document" : scope.kind,
298
504
  "."
299
505
  ] }),
300
- /* @__PURE__ */ jsx2("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
301
- ] }) : /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
302
- messages.map((m) => /* @__PURE__ */ jsx2(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
303
- streamContent && /* @__PURE__ */ jsx2(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
506
+ /* @__PURE__ */ jsx3("p", { className: "mt-1 text-xs text-muted-foreground", children: "It can read the current contents and propose edits, rewrites, or follow-ups." })
507
+ ] }) : /* @__PURE__ */ jsxs3("div", { className: "space-y-3", children: [
508
+ messages.map((m) => /* @__PURE__ */ jsx3(ChatMessage, { role: m.role, content: m.content, timestamp: m.createdAt }, m.id)),
509
+ streamContent && /* @__PURE__ */ jsx3(ChatMessage, { role: "assistant", content: streamContent, isStreaming: true })
304
510
  ] }) }),
305
- /* @__PURE__ */ jsx2(
511
+ /* @__PURE__ */ jsx3(
306
512
  "form",
307
513
  {
308
514
  className: "border-t border-border bg-background/60 p-3",
@@ -310,8 +516,8 @@ function ArtifactAgentDock({
310
516
  e.preventDefault();
311
517
  void send();
312
518
  },
313
- children: /* @__PURE__ */ jsxs2("div", { className: "flex items-end gap-2", children: [
314
- /* @__PURE__ */ jsx2(
519
+ children: /* @__PURE__ */ jsxs3("div", { className: "flex items-end gap-2", children: [
520
+ /* @__PURE__ */ jsx3(
315
521
  "textarea",
316
522
  {
317
523
  value: composer,
@@ -328,23 +534,23 @@ function ArtifactAgentDock({
328
534
  className: "min-h-[44px] flex-1 resize-none rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-primary/40 disabled:opacity-50"
329
535
  }
330
536
  ),
331
- sending ? /* @__PURE__ */ jsx2(
537
+ sending ? /* @__PURE__ */ jsx3(
332
538
  "button",
333
539
  {
334
540
  type: "button",
335
541
  onClick: cancel,
336
542
  "aria-label": "Cancel",
337
543
  className: "inline-flex h-9 w-9 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-muted hover:text-foreground",
338
- children: /* @__PURE__ */ jsx2(Square, { className: "h-4 w-4" })
544
+ children: /* @__PURE__ */ jsx3(Square, { className: "h-4 w-4" })
339
545
  }
340
- ) : /* @__PURE__ */ jsx2(
546
+ ) : /* @__PURE__ */ jsx3(
341
547
  "button",
342
548
  {
343
549
  type: "submit",
344
550
  disabled: !composer.trim() || !threadId,
345
551
  "aria-label": "Send",
346
552
  className: "inline-flex h-9 w-9 items-center justify-center rounded-md bg-primary text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
347
- children: /* @__PURE__ */ jsx2(Send, { className: "h-4 w-4" })
553
+ children: /* @__PURE__ */ jsx3(Send, { className: "h-4 w-4" })
348
554
  }
349
555
  )
350
556
  ] })
@@ -433,6 +639,12 @@ function createFetchTransport(opts) {
433
639
  export {
434
640
  DEFAULT_REASONING_LEVEL_OPTIONS,
435
641
  ReasoningLevelPicker,
642
+ HARNESS_MODEL_POLICIES,
643
+ modelProvider,
644
+ isModelCompatibleWithHarness,
645
+ snapModelToHarness,
646
+ snapHarnessToModel,
647
+ AgentSessionControls,
436
648
  ArtifactAgentDock,
437
649
  createFetchTransport,
438
650
  AgentTimeline,
@@ -1,6 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
- export { B as BillingBalance, a as BillingDashboard, b as BillingDashboardProps, c as BillingSubscription, d as BillingUsage, M as ModelInfo, e as ModelPicker, f as ModelPickerProps, g as ModelPickerVariant, P as PricingPage, h as PricingPageProps, i as PricingTier, T as TemplateCard, j as TemplateCardData, k as TemplateCardProps, U as UsageChart, l as UsageChartProps, m as UsageDataPoint, n as canonicalModelId, o as formatContext, p as formatPrice, q as formatPricing } from './template-card-gf-InrfN.js';
3
+ export { B as Backend, a as BackendSelector, b as BackendSelectorProps, H as HARNESS_OPTIONS, c as HarnessPicker, d as HarnessPickerProps, e as HarnessType } from './harness-picker-C1W3rTeb.js';
4
+ export { M as ModelInfo, a as ModelPicker, b as ModelPickerProps, c as ModelPickerVariant, d as canonicalModelId, f as formatContext, e as formatPricing } from './model-picker-DUfMTQo5.js';
5
+ export { B as BillingBalance, a as BillingDashboard, b as BillingDashboardProps, c as BillingSubscription, d as BillingUsage, P as PricingPage, e as PricingPageProps, f as PricingTier, T as TemplateCard, g as TemplateCardData, h as TemplateCardProps, U as UsageChart, i as UsageChartProps, j as UsageDataPoint, k as formatPrice } from './template-card-UhV3pmRC.js';
4
6
 
5
7
  interface SidebarUser {
6
8
  email: string;
@@ -344,52 +346,6 @@ interface PlanCardsProps {
344
346
  }
345
347
  declare function PlanCards({ plans, className }: PlanCardsProps): react_jsx_runtime.JSX.Element;
346
348
 
347
- interface Backend {
348
- type: string;
349
- label: string;
350
- description?: string;
351
- }
352
- interface BackendSelectorProps {
353
- backends: Backend[];
354
- selected: string;
355
- onChange: (selected: string) => void;
356
- label?: string;
357
- placeholder?: string;
358
- className?: string;
359
- }
360
- declare function BackendSelector({ backends, selected, onChange, label, placeholder, className, }: BackendSelectorProps): react_jsx_runtime.JSX.Element;
361
-
362
- /**
363
- * Sandbox agent harness types — mirrors `BackendType` from
364
- * `@tangle-network/sandbox` SDK. Kept in lockstep with that enum:
365
- * if the SDK adds a backend, add it here too.
366
- */
367
- type HarnessType = "opencode" | "claude-code" | "codex" | "amp" | "factory-droids" | "cli-base";
368
- interface HarnessOption extends Backend {
369
- type: HarnessType;
370
- }
371
- /**
372
- * Default option list with human-readable copy. Order is curated so the
373
- * recommended choice (`opencode`) appears first; `cli-base` (no agent) last.
374
- */
375
- declare const HARNESS_OPTIONS: readonly HarnessOption[];
376
- interface HarnessPickerProps extends Omit<BackendSelectorProps, "backends" | "selected" | "onChange"> {
377
- value: HarnessType;
378
- onChange: (next: HarnessType) => void;
379
- /** Filter the available harnesses (e.g. by plan tier). Defaults to all. */
380
- available?: ReadonlyArray<HarnessType>;
381
- /** Override or extend the option metadata. Keys are HarnessType. */
382
- optionsOverride?: Partial<Record<HarnessType, Partial<Omit<HarnessOption, "type">>>>;
383
- }
384
- /**
385
- * Type-safe harness/backend selector for sandbox-backed agent products.
386
- *
387
- * Wraps the generic {@link BackendSelector} with the canonical harness list
388
- * baked in, so consumers don't have to re-declare it (or risk drifting from
389
- * the SDK enum).
390
- */
391
- declare function HarnessPicker({ value, onChange, available, optionsOverride, label, ...rest }: HarnessPickerProps): react_jsx_runtime.JSX.Element;
392
-
393
349
  type ProductVariant = "sandbox";
394
350
  interface NavItem {
395
351
  id: string;
@@ -495,6 +451,39 @@ interface ProfileComparisonProps {
495
451
  }
496
452
  declare function ProfileComparison({ profiles, className, }: ProfileComparisonProps): react_jsx_runtime.JSX.Element | null;
497
453
 
454
+ interface MetricChartPoint {
455
+ /** Wall-clock ms. */
456
+ at: number;
457
+ /** Null renders a gap in the series, never a zero. */
458
+ value: number | null;
459
+ }
460
+ type MetricChartTone = "primary" | "success" | "warning" | "danger";
461
+ interface MetricAreaChartProps {
462
+ points: MetricChartPoint[];
463
+ label: string;
464
+ /** Formats the current value and y-axis bounds (e.g. percent, bytes). */
465
+ formatValue: (value: number) => string;
466
+ /**
467
+ * Fixed y-axis maximum (100 for percents, memory total for bytes).
468
+ * Omitted = auto-scale to the observed maximum with 10% headroom.
469
+ */
470
+ maxValue?: number;
471
+ /** Secondary line under the current value (e.g. "of 8 GiB"). */
472
+ detail?: React.ReactNode;
473
+ tone?: MetricChartTone;
474
+ /** Plot height in px. Defaults to 96. */
475
+ height?: number;
476
+ /** Shown instead of the plot while no point has a value yet. */
477
+ emptyState?: React.ReactNode;
478
+ className?: string;
479
+ }
480
+ /**
481
+ * Lightweight live time-series panel (Prometheus/Grafana idiom): big
482
+ * current value, area chart of the rolling window, dashed gridlines.
483
+ * Pure SVG — no chart dependency. Null samples render as gaps.
484
+ */
485
+ declare function MetricAreaChart({ points, label, formatValue, maxValue, detail, tone, height, emptyState, className, }: MetricAreaChartProps): react_jsx_runtime.JSX.Element;
486
+
498
487
  type VariantStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
499
488
  type VariantOutcome = "pending_review" | "accepted" | "rejected" | "merged_with_conflicts" | "expired";
500
489
  interface Variant {
@@ -670,4 +659,4 @@ interface InfoPanelProps {
670
659
  }
671
660
  declare function InfoPanel({ label, title, description, className }: InfoPanelProps): react_jsx_runtime.JSX.Element;
672
661
 
673
- export { type Backend, BackendConfig, type BackendConfigProps, BackendSelector, type BackendSelectorProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type ExposedPort, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, HARNESS_OPTIONS, HarnessPicker, type HarnessPickerProps, type HarnessType, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, PortsList, type PortsListProps, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, ResourceMeter, type ResourceMeterProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, parseInsufficientBalance, useSidebar };
662
+ export { BackendConfig, type BackendConfigProps, type BackendStatusData, ClusterStatusBar, type ClusterStatusBarProps, type ClusterStatusItem, CreditBalance, type CreditBalanceProps, DashboardLayout, type DashboardLayoutProps, type Profile as DashboardProfile, type SnapshotInfo as DashboardSnapshotInfo, type DashboardUser, type ExposedPort, type GitCommitData, GitPanel, type GitPanelProps, type GitStatusData, INSUFFICIENT_BALANCE_CODE, InfoPanel, type InfoPanelProps, type InsufficientBalance, type Invoice, InvoiceTable, type InvoiceTableProps, type McpServer, MetricAreaChart, type MetricAreaChartProps, type MetricChartPoint, type MetricChartTone, type NavItem, NetworkConfig, type NetworkConfigData, type NetworkConfigProps, NewSandboxCard, type NewSandboxCardProps, OutOfCreditsModal, type OutOfCreditsModalProps, type PlanCardData, PlanCards, type PlanCardsProps, type PlanFeature, PortsList, type PortsListProps, type ProcessInfo, ProcessList, type ProcessListProps, type ProductVariant, ProfileAvatar, type ProfileAvatarProps, ProfileComparison, type ProfileComparisonProps, ProfileSelector, type ProfileSelectorProps, PromoBanner, type PromoBannerProps, RailButton, type RailButtonProps, RailModeButton, type RailModeButtonProps, RailSeparator, type RailSeparatorProps, ResourceMeter, type ResourceMeterProps, SIDEBAR_MOBILE_WIDTH, SIDEBAR_PANEL_WIDTH, SIDEBAR_RAIL_WIDTH, SIDEBAR_TOTAL_WIDTH, SandboxCard, type SandboxCardData, type SandboxCardProps, type SandboxStatus, SandboxTable, type SandboxTableProps, Sidebar, SidebarContent, type SidebarContentProps, SidebarPanel, SidebarPanelContent, type SidebarPanelContentProps, SidebarPanelHeader, type SidebarPanelHeaderProps, type SidebarPanelProps, type SidebarProps, SidebarProvider, type SidebarProviderProps, SidebarRail, SidebarRailFooter, type SidebarRailFooterProps, SidebarRailHeader, type SidebarRailHeaderProps, SidebarRailNav, type SidebarRailNavProps, type SidebarRailProps, type SidebarUser, SnapshotList, type SnapshotListProps, SystemLogsViewer, type SystemLogsViewerProps, type TeamRole, UsageSummary, type UsageSummaryData, type UsageSummaryProps, type Variant, VariantList, type VariantListProps, type VariantOutcome, type VariantStatus, canAdminSandbox, parseInsufficientBalance, useSidebar };
package/dist/dashboard.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import {
2
2
  BackendConfig,
3
- BackendSelector,
4
3
  ClusterStatusBar,
5
4
  CreditBalance,
6
5
  DashboardLayout,
7
6
  GitPanel,
8
- HARNESS_OPTIONS,
9
- HarnessPicker,
10
7
  INSUFFICIENT_BALANCE_CODE,
11
8
  InvoiceTable,
9
+ MetricAreaChart,
12
10
  NetworkConfig,
13
11
  NewSandboxCard,
14
12
  OutOfCreditsModal,
@@ -46,20 +44,27 @@ import {
46
44
  canAdminSandbox,
47
45
  parseInsufficientBalance,
48
46
  useSidebar
49
- } from "./chunk-R6QNJQRH.js";
47
+ } from "./chunk-LA5GHELP.js";
50
48
  import {
51
49
  BillingDashboard,
52
50
  InfoPanel,
53
- ModelPicker,
54
51
  PricingPage,
55
52
  TemplateCard,
56
53
  UsageChart,
54
+ formatPrice
55
+ } from "./chunk-DNZ4DTNA.js";
56
+ import "./chunk-7ZA5SEK3.js";
57
+ import {
58
+ BackendSelector,
59
+ HARNESS_OPTIONS,
60
+ HarnessPicker
61
+ } from "./chunk-ESRYVGHF.js";
62
+ import {
63
+ ModelPicker,
57
64
  canonicalModelId,
58
65
  formatContext,
59
- formatPrice,
60
66
  formatPricing
61
- } from "./chunk-QNVVKMEW.js";
62
- import "./chunk-7ZA5SEK3.js";
67
+ } from "./chunk-4KAPMTPU.js";
63
68
  import "./chunk-EI44GEQ5.js";
64
69
  export {
65
70
  BackendConfig,
@@ -74,6 +79,7 @@ export {
74
79
  INSUFFICIENT_BALANCE_CODE,
75
80
  InfoPanel,
76
81
  InvoiceTable,
82
+ MetricAreaChart,
77
83
  ModelPicker,
78
84
  NetworkConfig,
79
85
  NewSandboxCard,