@stigmer/react 3.7.0 → 3.9.0
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/attachment/AttachmentChipList.d.ts.map +1 -1
- package/attachment/AttachmentChipList.js +30 -1
- package/attachment/AttachmentChipList.js.map +1 -1
- package/attachment/attachment-utils.d.ts +11 -0
- package/attachment/attachment-utils.d.ts.map +1 -1
- package/attachment/attachment-utils.js +23 -0
- package/attachment/attachment-utils.js.map +1 -1
- package/attachment/clipboard.d.ts +51 -0
- package/attachment/clipboard.d.ts.map +1 -0
- package/attachment/clipboard.js +89 -0
- package/attachment/clipboard.js.map +1 -0
- package/attachment/index.d.ts +6 -1
- package/attachment/index.d.ts.map +1 -1
- package/attachment/index.js +4 -1
- package/attachment/index.js.map +1 -1
- package/attachment/prepare-image.d.ts +43 -0
- package/attachment/prepare-image.d.ts.map +1 -0
- package/attachment/prepare-image.js +162 -0
- package/attachment/prepare-image.js.map +1 -0
- package/attachment/useAttachments.d.ts.map +1 -1
- package/attachment/useAttachments.js +23 -3
- package/attachment/useAttachments.js.map +1 -1
- package/attachment/vision-fit.d.ts +50 -0
- package/attachment/vision-fit.d.ts.map +1 -0
- package/attachment/vision-fit.js +76 -0
- package/attachment/vision-fit.js.map +1 -0
- package/channel/AgentChannelsPanel.d.ts.map +1 -1
- package/channel/AgentChannelsPanel.js +21 -2
- package/channel/AgentChannelsPanel.js.map +1 -1
- package/channel/ChannelConversationsDialog.d.ts +12 -4
- package/channel/ChannelConversationsDialog.d.ts.map +1 -1
- package/channel/ChannelConversationsDialog.js +18 -24
- package/channel/ChannelConversationsDialog.js.map +1 -1
- package/channel/providers.d.ts +23 -0
- package/channel/providers.d.ts.map +1 -1
- package/channel/providers.js +4 -0
- package/channel/providers.js.map +1 -1
- package/channel/useSaveAgentChannel.d.ts +5 -4
- package/channel/useSaveAgentChannel.d.ts.map +1 -1
- package/channel/useSaveAgentChannel.js +18 -4
- package/channel/useSaveAgentChannel.js.map +1 -1
- package/composer/ComposerToolbar.d.ts +1 -1
- package/composer/ComposerToolbar.d.ts.map +1 -1
- package/composer/SessionComposer.d.ts +4 -2
- package/composer/SessionComposer.d.ts.map +1 -1
- package/composer/SessionComposer.js +51 -5
- package/composer/SessionComposer.js.map +1 -1
- package/conversation/ConversationAttentionBanner.d.ts +22 -0
- package/conversation/ConversationAttentionBanner.d.ts.map +1 -0
- package/conversation/ConversationAttentionBanner.js +25 -0
- package/conversation/ConversationAttentionBanner.js.map +1 -0
- package/conversation/ConversationComposer.d.ts +41 -0
- package/conversation/ConversationComposer.d.ts.map +1 -0
- package/conversation/ConversationComposer.js +57 -0
- package/conversation/ConversationComposer.js.map +1 -0
- package/conversation/ConversationControlBanner.d.ts +52 -0
- package/conversation/ConversationControlBanner.d.ts.map +1 -0
- package/conversation/ConversationControlBanner.js +59 -0
- package/conversation/ConversationControlBanner.js.map +1 -0
- package/conversation/ConversationListPane.d.ts +68 -0
- package/conversation/ConversationListPane.d.ts.map +1 -0
- package/conversation/ConversationListPane.js +120 -0
- package/conversation/ConversationListPane.js.map +1 -0
- package/conversation/ConversationTimelineView.d.ts +46 -0
- package/conversation/ConversationTimelineView.d.ts.map +1 -0
- package/conversation/ConversationTimelineView.js +166 -0
- package/conversation/ConversationTimelineView.js.map +1 -0
- package/conversation/ConversationsWorkbench.d.ts +63 -0
- package/conversation/ConversationsWorkbench.d.ts.map +1 -0
- package/conversation/ConversationsWorkbench.js +165 -0
- package/conversation/ConversationsWorkbench.js.map +1 -0
- package/conversation/conversationPresentation.d.ts +137 -0
- package/conversation/conversationPresentation.d.ts.map +1 -0
- package/conversation/conversationPresentation.js +230 -0
- package/conversation/conversationPresentation.js.map +1 -0
- package/conversation/index.d.ts +26 -0
- package/conversation/index.d.ts.map +1 -0
- package/conversation/index.js +14 -0
- package/conversation/index.js.map +1 -0
- package/conversation/polling.d.ts +39 -0
- package/conversation/polling.d.ts.map +1 -0
- package/conversation/polling.js +39 -0
- package/conversation/polling.js.map +1 -0
- package/conversation/useConversation.d.ts +61 -0
- package/conversation/useConversation.d.ts.map +1 -0
- package/conversation/useConversation.js +153 -0
- package/conversation/useConversation.js.map +1 -0
- package/conversation/useConversationList.d.ts +98 -0
- package/conversation/useConversationList.d.ts.map +1 -0
- package/conversation/useConversationList.js +200 -0
- package/conversation/useConversationList.js.map +1 -0
- package/conversation/useConversationParticipation.d.ts +63 -0
- package/conversation/useConversationParticipation.d.ts.map +1 -0
- package/conversation/useConversationParticipation.js +84 -0
- package/conversation/useConversationParticipation.js.map +1 -0
- package/conversation/useConversationTimeline.d.ts +71 -0
- package/conversation/useConversationTimeline.d.ts.map +1 -0
- package/conversation/useConversationTimeline.js +152 -0
- package/conversation/useConversationTimeline.js.map +1 -0
- package/conversation/useConversationsWantsHumanCount.d.ts +42 -0
- package/conversation/useConversationsWantsHumanCount.d.ts.map +1 -0
- package/conversation/useConversationsWantsHumanCount.js +40 -0
- package/conversation/useConversationsWantsHumanCount.js.map +1 -0
- package/index.d.ts +6 -2
- package/index.d.ts.map +1 -1
- package/index.js +11 -2
- package/index.js.map +1 -1
- package/internal/tooltip.d.ts.map +1 -1
- package/internal/tooltip.js +7 -1
- package/internal/tooltip.js.map +1 -1
- package/internal/useAutoScroll.d.ts +25 -4
- package/internal/useAutoScroll.d.ts.map +1 -1
- package/internal/useAutoScroll.js +55 -15
- package/internal/useAutoScroll.js.map +1 -1
- package/internal/useFetch.d.ts.map +1 -1
- package/internal/useFetch.js +14 -4
- package/internal/useFetch.js.map +1 -1
- package/models/ModelSelector.d.ts +24 -5
- package/models/ModelSelector.d.ts.map +1 -1
- package/models/ModelSelector.js +31 -9
- package/models/ModelSelector.js.map +1 -1
- package/models/registry.d.ts +3 -2
- package/models/registry.d.ts.map +1 -1
- package/models/registry.js.map +1 -1
- package/models/service-tier.d.ts +1 -1
- package/models/service-tier.d.ts.map +1 -1
- package/models/service-tier.js +1 -1
- package/models/service-tier.js.map +1 -1
- package/package.json +4 -4
- package/schedule/ScheduleDetailView.js +1 -1
- package/schedule/ScheduleDetailView.js.map +1 -1
- package/schedule/ScheduleForm.d.ts.map +1 -1
- package/schedule/ScheduleForm.js +7 -6
- package/schedule/ScheduleForm.js.map +1 -1
- package/sharing/useSaveAgentShare.d.ts +10 -1
- package/sharing/useSaveAgentShare.d.ts.map +1 -1
- package/sharing/useSaveAgentShare.js +9 -0
- package/sharing/useSaveAgentShare.js.map +1 -1
- package/sidebar/WorkspaceSidebar.d.ts +12 -4
- package/sidebar/WorkspaceSidebar.d.ts.map +1 -1
- package/sidebar/WorkspaceSidebar.js +19 -7
- package/sidebar/WorkspaceSidebar.js.map +1 -1
- package/src/attachment/AttachmentChipList.tsx +45 -1
- package/src/attachment/__tests__/AttachmentChipList.test.tsx +103 -0
- package/src/attachment/__tests__/attachment-utils.test.ts +54 -0
- package/src/attachment/__tests__/clipboard.test.ts +110 -0
- package/src/attachment/__tests__/prepare-image.browser.test.ts +166 -0
- package/src/attachment/__tests__/prepare-image.test.ts +32 -0
- package/src/attachment/__tests__/vision-fit.test.ts +93 -0
- package/src/attachment/attachment-utils.ts +27 -0
- package/src/attachment/clipboard.ts +102 -0
- package/src/attachment/index.ts +13 -0
- package/src/attachment/prepare-image.ts +179 -0
- package/src/attachment/useAttachments.ts +26 -2
- package/src/attachment/vision-fit.ts +90 -0
- package/src/channel/AgentChannelsPanel.tsx +33 -2
- package/src/channel/ChannelConversationsDialog.tsx +22 -25
- package/src/channel/__tests__/AgentChannelsPanel.test.tsx +36 -5
- package/src/channel/__tests__/ChannelConversationsDialog.test.tsx +38 -1
- package/src/channel/__tests__/useSaveAgentChannel.test.tsx +26 -0
- package/src/channel/providers.ts +27 -0
- package/src/channel/useSaveAgentChannel.ts +18 -4
- package/src/composer/ComposerToolbar.tsx +1 -1
- package/src/composer/SessionComposer.tsx +75 -6
- package/src/composer/__tests__/SessionComposer-paste.test.tsx +263 -0
- package/src/composer/__tests__/SessionComposer-serviceTier.test.tsx +4 -3
- package/src/composer/__tests__/SessionComposer-uploadGate.test.tsx +239 -0
- package/src/conversation/ConversationAttentionBanner.tsx +77 -0
- package/src/conversation/ConversationComposer.tsx +156 -0
- package/src/conversation/ConversationControlBanner.tsx +193 -0
- package/src/conversation/ConversationListPane.tsx +459 -0
- package/src/conversation/ConversationTimelineView.tsx +435 -0
- package/src/conversation/ConversationsWorkbench.tsx +378 -0
- package/src/conversation/__tests__/ConversationAttentionBanner.test.tsx +82 -0
- package/src/conversation/__tests__/ConversationComposer.test.tsx +160 -0
- package/src/conversation/__tests__/ConversationControlBanner.test.tsx +190 -0
- package/src/conversation/__tests__/ConversationListPane.test.tsx +372 -0
- package/src/conversation/__tests__/ConversationTimelineView.layout.test.tsx +160 -0
- package/src/conversation/__tests__/ConversationTimelineView.test.tsx +405 -0
- package/src/conversation/__tests__/ConversationsWorkbench.test.tsx +860 -0
- package/src/conversation/__tests__/a11y/conversation-surfaces.a11y.test.tsx +235 -0
- package/src/conversation/__tests__/a11y/harness.tsx +100 -0
- package/src/conversation/__tests__/conversation-tooltips.layout.test.tsx +167 -0
- package/src/conversation/__tests__/conversationPollingComposition.test.tsx +138 -0
- package/src/conversation/__tests__/conversationPresentation.test.ts +289 -0
- package/src/conversation/__tests__/useConversation.test.tsx +243 -0
- package/src/conversation/__tests__/useConversationList.test.tsx +492 -0
- package/src/conversation/__tests__/useConversationParticipation.test.tsx +190 -0
- package/src/conversation/__tests__/useConversationTimeline.test.tsx +258 -0
- package/src/conversation/__tests__/useConversationsWantsHumanCount.test.tsx +102 -0
- package/src/conversation/conversationPresentation.ts +305 -0
- package/src/conversation/index.ts +80 -0
- package/src/conversation/polling.ts +41 -0
- package/src/conversation/useConversation.ts +221 -0
- package/src/conversation/useConversationList.ts +328 -0
- package/src/conversation/useConversationParticipation.ts +178 -0
- package/src/conversation/useConversationTimeline.ts +244 -0
- package/src/conversation/useConversationsWantsHumanCount.ts +86 -0
- package/src/index.ts +78 -1
- package/src/internal/__tests__/fake-timer-slices.ts +29 -0
- package/src/internal/__tests__/useAutoScroll.layout.test.tsx +143 -0
- package/src/internal/__tests__/useAutoScroll.test.tsx +62 -5
- package/src/internal/__tests__/useFetch-poll-under-render-pressure.test.tsx +79 -0
- package/src/internal/tooltip.tsx +6 -1
- package/src/internal/useAutoScroll.ts +66 -20
- package/src/internal/useFetch.ts +15 -4
- package/src/models/ModelSelector.tsx +81 -49
- package/src/models/__tests__/ModelSelector-serviceTier.test.tsx +105 -21
- package/src/models/registry.ts +3 -2
- package/src/models/service-tier.ts +1 -1
- package/src/schedule/ScheduleDetailView.tsx +1 -1
- package/src/schedule/ScheduleForm.tsx +7 -6
- package/src/schedule/__tests__/scheduleCreation.test.tsx +2 -2
- package/src/sharing/__tests__/useSaveAgentShare.test.tsx +55 -1
- package/src/sharing/__tests__/useShareToolReadiness.test.tsx +1 -0
- package/src/sharing/useSaveAgentShare.ts +19 -1
- package/src/sidebar/WorkspaceSidebar.tsx +62 -3
- package/src/sidebar/__tests__/WorkspaceSidebar.test.tsx +56 -1
- package/styles.css +1 -1
|
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
4
4
|
import { Popover } from "@base-ui/react/popover";
|
|
5
5
|
import { cn } from "@stigmer/theme";
|
|
6
6
|
import { useStigmerPortalContainer } from "../portal-container.js";
|
|
7
|
+
import { Switch } from "../switch/Switch.js";
|
|
7
8
|
import { useModelRegistry } from "./useModelRegistry.js";
|
|
8
9
|
import type { ModelInfo, CostTier, SpeedTier } from "./registry.js";
|
|
9
10
|
import { HARNESS_META, HARNESS_OPTIONS, type HarnessOption } from "./harness.js";
|
|
@@ -80,11 +81,17 @@ export interface ModelSelectorProps {
|
|
|
80
81
|
readonly serviceTier?: ServiceTierOption;
|
|
81
82
|
/**
|
|
82
83
|
* Called when the user toggles the fast tier. Providing this enables the
|
|
83
|
-
* tier
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
84
|
+
* "Fast tier" switch in the popover's options area (top of the popover,
|
|
85
|
+
* beside the harness row — the Cursor options-panel convention), which
|
|
86
|
+
* renders ONLY while the selected model prices a fast variant
|
|
87
|
+
* ({@link ModelInfo.serviceTiers}) — no dead controls.
|
|
88
|
+
*
|
|
89
|
+
* An active fast tier persists across switches between fast-capable
|
|
90
|
+
* models (the tier is always visible: trigger badge + options switch),
|
|
91
|
+
* so "fast, but on that other model" is one action, not a re-toggle.
|
|
92
|
+
* Selecting a model without a fast tier resets to "standard" through
|
|
93
|
+
* this callback — an unsupported combination can never be submitted
|
|
94
|
+
* (#357, refused at create time).
|
|
88
95
|
*/
|
|
89
96
|
readonly onServiceTierChange?: (tier: ServiceTierOption) => void;
|
|
90
97
|
|
|
@@ -105,6 +112,19 @@ export interface ModelSelectorProps {
|
|
|
105
112
|
* The trigger button displays the current selection in compact format:
|
|
106
113
|
* `Harness · Model Name ▾` (or just `Model Name ▾` when harness is locked).
|
|
107
114
|
*
|
|
115
|
+
* **Service tier (#357).** When the consumer opts in via
|
|
116
|
+
* {@link ModelSelectorProps.onServiceTierChange} and the selected model
|
|
117
|
+
* prices a fast variant, a "Fast tier" switch renders in the options area
|
|
118
|
+
* at the TOP of the popover — the first thing visible on open, mirroring
|
|
119
|
+
* Cursor's own options panel (Jakob's Law: our users already know that
|
|
120
|
+
* layout). Deliberately not a per-row affordance: row-level chips crowd
|
|
121
|
+
* the rows and collide with the "Fast/Fastest" speed badge, and an
|
|
122
|
+
* interactive element inside `role="option"` is an ARIA authoring error
|
|
123
|
+
* (option children are presentational). An active fast tier persists
|
|
124
|
+
* across switches between fast-capable models — the visible badge and
|
|
125
|
+
* switch keep the state honest — and resets only for models with no fast
|
|
126
|
+
* variant, where it would be refused at create time.
|
|
127
|
+
*
|
|
108
128
|
* @example
|
|
109
129
|
* ```tsx
|
|
110
130
|
* <ModelSelector
|
|
@@ -245,9 +265,11 @@ export function ModelSelector({
|
|
|
245
265
|
const selectModel = useCallback(
|
|
246
266
|
(model: ModelInfo) => {
|
|
247
267
|
onValueChange(model.modelId);
|
|
248
|
-
//
|
|
249
|
-
//
|
|
250
|
-
//
|
|
268
|
+
// An active fast tier survives a switch between fast-capable models —
|
|
269
|
+
// resetting it forced users to re-toggle on every model change, and
|
|
270
|
+
// the tier is never silent (trigger badge + options switch both show
|
|
271
|
+
// it). It resets ONLY when the new model prices no fast variant: a
|
|
272
|
+
// stale tier there would be refused at create time (#357).
|
|
251
273
|
if (
|
|
252
274
|
serviceTier === "fast"
|
|
253
275
|
&& onServiceTierChange
|
|
@@ -268,7 +290,7 @@ export function ModelSelector({
|
|
|
268
290
|
}, []);
|
|
269
291
|
|
|
270
292
|
const handleKeyDown = useCallback(
|
|
271
|
-
(e: React.KeyboardEvent) => {
|
|
293
|
+
(e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
272
294
|
const len = visibleModels.length;
|
|
273
295
|
if (len === 0) return;
|
|
274
296
|
|
|
@@ -304,14 +326,18 @@ export function ModelSelector({
|
|
|
304
326
|
|
|
305
327
|
const showShowAllButton = !isSearching && !showAll && featuredModels.length > 0 && featuredModels.length < models.length;
|
|
306
328
|
|
|
307
|
-
// The
|
|
308
|
-
// the selected model prices a fast variant (recognition over
|
|
309
|
-
// never a dead control on models with no tier choice).
|
|
329
|
+
// The options-area switch exists only where it applies: a consumer opted
|
|
330
|
+
// in AND the selected model prices a fast variant (recognition over
|
|
331
|
+
// recall — never a dead control on models with no tier choice).
|
|
310
332
|
const showServiceTierToggle =
|
|
311
333
|
onServiceTierChange !== undefined
|
|
312
334
|
&& selectedModel !== undefined
|
|
313
335
|
&& selectedModel.serviceTiers.includes(FAST_SERVICE_TIER);
|
|
314
|
-
|
|
336
|
+
// The trigger badge is display-only: fast is in effect iff the tier is
|
|
337
|
+
// fast AND the selected model actually prices the variant.
|
|
338
|
+
const fastActive =
|
|
339
|
+
serviceTier === "fast"
|
|
340
|
+
&& (selectedModel?.serviceTiers.includes(FAST_SERVICE_TIER) ?? false);
|
|
315
341
|
|
|
316
342
|
const usingPlaceholder = !value && placeholderLabel !== undefined;
|
|
317
343
|
const triggerLabel = usingPlaceholder
|
|
@@ -342,13 +368,14 @@ export function ModelSelector({
|
|
|
342
368
|
<span className="shrink-0 text-border" aria-hidden>·</span>
|
|
343
369
|
)}
|
|
344
370
|
<span className="truncate">{triggerLabel}</span>
|
|
345
|
-
{
|
|
371
|
+
{fastActive && (
|
|
346
372
|
<span
|
|
347
373
|
className={cn(
|
|
348
|
-
"shrink-0 rounded-
|
|
349
|
-
"text-[0.6rem] font-medium uppercase tracking-wider text-
|
|
374
|
+
"inline-flex shrink-0 items-center gap-0.5 rounded-full border border-primary px-1.5 py-px",
|
|
375
|
+
"text-[0.6rem] font-medium uppercase tracking-wider text-primary",
|
|
350
376
|
)}
|
|
351
377
|
>
|
|
378
|
+
<BoltIcon />
|
|
352
379
|
Fast
|
|
353
380
|
</span>
|
|
354
381
|
)}
|
|
@@ -433,6 +460,29 @@ export function ModelSelector({
|
|
|
433
460
|
)}
|
|
434
461
|
</div>
|
|
435
462
|
|
|
463
|
+
{/* Options area — the Cursor options-panel convention: settings
|
|
464
|
+
of the CURRENT selection sit at the top of the popover, the
|
|
465
|
+
first thing visible on open, never buried under the list.
|
|
466
|
+
Rendered only while the selected model prices a fast
|
|
467
|
+
variant (#357). */}
|
|
468
|
+
{showServiceTierToggle && (
|
|
469
|
+
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
|
470
|
+
<div className="flex flex-col">
|
|
471
|
+
<span className="text-xs text-foreground">Fast tier</span>
|
|
472
|
+
<span className="text-[0.65rem] text-muted-foreground">
|
|
473
|
+
Faster responses at higher per-token rates
|
|
474
|
+
</span>
|
|
475
|
+
</div>
|
|
476
|
+
<Switch
|
|
477
|
+
checked={fastActive}
|
|
478
|
+
onCheckedChange={(next) =>
|
|
479
|
+
onServiceTierChange?.(next ? "fast" : "standard")
|
|
480
|
+
}
|
|
481
|
+
aria-label="Fast tier"
|
|
482
|
+
/>
|
|
483
|
+
</div>
|
|
484
|
+
)}
|
|
485
|
+
|
|
436
486
|
{/* Search input */}
|
|
437
487
|
<div className="border-b border-border px-3 py-1.5">
|
|
438
488
|
<input
|
|
@@ -535,39 +585,6 @@ export function ModelSelector({
|
|
|
535
585
|
</button>
|
|
536
586
|
)}
|
|
537
587
|
</div>
|
|
538
|
-
|
|
539
|
-
{/* Service tier toggle — only for models that price a fast variant (#357) */}
|
|
540
|
-
{showServiceTierToggle && (
|
|
541
|
-
<div className="flex items-center justify-between border-t border-border px-3 py-2">
|
|
542
|
-
<div className="flex flex-col">
|
|
543
|
-
<span className="text-xs text-foreground">Fast tier</span>
|
|
544
|
-
<span className="text-[0.65rem] text-muted-foreground">
|
|
545
|
-
Faster responses at higher per-token rates
|
|
546
|
-
</span>
|
|
547
|
-
</div>
|
|
548
|
-
<button
|
|
549
|
-
type="button"
|
|
550
|
-
role="switch"
|
|
551
|
-
aria-checked={fastActive}
|
|
552
|
-
aria-label="Fast tier"
|
|
553
|
-
className={cn(
|
|
554
|
-
"relative h-4.5 w-8 shrink-0 rounded-full transition-colors cursor-pointer",
|
|
555
|
-
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
556
|
-
fastActive ? "bg-primary" : "bg-muted",
|
|
557
|
-
)}
|
|
558
|
-
onClick={() =>
|
|
559
|
-
onServiceTierChange?.(fastActive ? "standard" : "fast")
|
|
560
|
-
}
|
|
561
|
-
>
|
|
562
|
-
<span
|
|
563
|
-
className={cn(
|
|
564
|
-
"absolute top-0.5 size-3.5 rounded-full bg-background shadow-sm transition-transform",
|
|
565
|
-
fastActive ? "translate-x-4" : "translate-x-0.5",
|
|
566
|
-
)}
|
|
567
|
-
/>
|
|
568
|
-
</button>
|
|
569
|
-
</div>
|
|
570
|
-
)}
|
|
571
588
|
</Popover.Popup>
|
|
572
589
|
</Popover.Positioner>
|
|
573
590
|
</Popover.Portal>
|
|
@@ -665,3 +682,18 @@ function CheckIcon({ className }: { className?: string }) {
|
|
|
665
682
|
</svg>
|
|
666
683
|
);
|
|
667
684
|
}
|
|
685
|
+
|
|
686
|
+
function BoltIcon() {
|
|
687
|
+
return (
|
|
688
|
+
<svg
|
|
689
|
+
className="shrink-0"
|
|
690
|
+
width="8"
|
|
691
|
+
height="10"
|
|
692
|
+
viewBox="0 0 8 10"
|
|
693
|
+
fill="currentColor"
|
|
694
|
+
aria-hidden
|
|
695
|
+
>
|
|
696
|
+
<path d="M4.9 0.2L0.6 5.6h2.6L3.1 9.8l4.3-5.4H4.8L4.9 0.2z" />
|
|
697
|
+
</svg>
|
|
698
|
+
);
|
|
699
|
+
}
|
|
@@ -23,12 +23,16 @@ beforeAll(() => {
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
*
|
|
26
|
+
* Fast-tier switch in the popover's options area (stigmer/stigmer#357).
|
|
27
27
|
*
|
|
28
|
-
* The
|
|
29
|
-
*
|
|
30
|
-
* (
|
|
31
|
-
*
|
|
28
|
+
* The switch sits at the TOP of the popover (the Cursor options-panel
|
|
29
|
+
* convention) and is capability-gated: it renders only when the consumer
|
|
30
|
+
* opted in (onServiceTierChange) AND the selected model prices a fast
|
|
31
|
+
* variant (ModelInfo.serviceTiers) — no dead controls. An active fast
|
|
32
|
+
* tier persists across switches between fast-capable models (the trigger
|
|
33
|
+
* badge and the switch keep the state visible), and resets ONLY when the
|
|
34
|
+
* chosen model prices no fast variant — a stale tier there would be
|
|
35
|
+
* refused at create time.
|
|
32
36
|
*/
|
|
33
37
|
|
|
34
38
|
const FAST_CAPABLE: ModelInfo = {
|
|
@@ -43,6 +47,18 @@ const FAST_CAPABLE: ModelInfo = {
|
|
|
43
47
|
serviceTiers: ["fast"],
|
|
44
48
|
};
|
|
45
49
|
|
|
50
|
+
const ALSO_FAST_CAPABLE: ModelInfo = {
|
|
51
|
+
modelId: "claude-opus-4-8",
|
|
52
|
+
provider: "anthropic",
|
|
53
|
+
displayName: "Claude 4.8 Opus",
|
|
54
|
+
shortDescription: "Frontier Opus via Cursor",
|
|
55
|
+
speedTier: "slow",
|
|
56
|
+
costTier: "premium",
|
|
57
|
+
harness: "cursor",
|
|
58
|
+
featured: true,
|
|
59
|
+
serviceTiers: ["fast"],
|
|
60
|
+
};
|
|
61
|
+
|
|
46
62
|
const NO_FAST_TIER: ModelInfo = {
|
|
47
63
|
modelId: "claude-haiku-4-5",
|
|
48
64
|
provider: "anthropic",
|
|
@@ -64,7 +80,7 @@ function renderSelector(
|
|
|
64
80
|
const result = render(
|
|
65
81
|
<ModelRegistryContext.Provider
|
|
66
82
|
value={{
|
|
67
|
-
models: [FAST_CAPABLE, NO_FAST_TIER],
|
|
83
|
+
models: [FAST_CAPABLE, ALSO_FAST_CAPABLE, NO_FAST_TIER],
|
|
68
84
|
isLoading: false,
|
|
69
85
|
error: null,
|
|
70
86
|
refetch: vi.fn(),
|
|
@@ -106,8 +122,8 @@ function modelOption(displayName: string): HTMLElement {
|
|
|
106
122
|
|
|
107
123
|
afterEach(cleanup);
|
|
108
124
|
|
|
109
|
-
describe("ModelSelector —
|
|
110
|
-
it("renders the
|
|
125
|
+
describe("ModelSelector — fast tier switch in the options area", () => {
|
|
126
|
+
it("renders the switch for a fast-capable selected model", async () => {
|
|
111
127
|
renderSelector();
|
|
112
128
|
await openPopover("Composer 2.5");
|
|
113
129
|
|
|
@@ -115,14 +131,28 @@ describe("ModelSelector — service tier toggle", () => {
|
|
|
115
131
|
expect(toggle.getAttribute("aria-checked")).toBe("false");
|
|
116
132
|
});
|
|
117
133
|
|
|
118
|
-
it("
|
|
134
|
+
it("places the switch in the options area above the model list", async () => {
|
|
135
|
+
renderSelector();
|
|
136
|
+
await openPopover("Composer 2.5");
|
|
137
|
+
|
|
138
|
+
const toggle = screen.getByRole("switch", { name: "Fast tier" });
|
|
139
|
+
const search = screen.getByRole("searchbox", { name: "Search models" });
|
|
140
|
+
// DOCUMENT_POSITION_FOLLOWING: the search input comes after the switch,
|
|
141
|
+
// i.e. the options area precedes search + list (never a footer).
|
|
142
|
+
expect(
|
|
143
|
+
toggle.compareDocumentPosition(search)
|
|
144
|
+
& Node.DOCUMENT_POSITION_FOLLOWING,
|
|
145
|
+
).toBeTruthy();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("does not render the switch for a model without a fast tier", async () => {
|
|
119
149
|
renderSelector({ value: "claude-haiku-4-5" });
|
|
120
150
|
await openPopover("Haiku 4.5");
|
|
121
151
|
|
|
122
152
|
expect(screen.queryByRole("switch", { name: "Fast tier" })).toBeNull();
|
|
123
153
|
});
|
|
124
154
|
|
|
125
|
-
it("does not render the
|
|
155
|
+
it("does not render the switch when the consumer did not opt in", async () => {
|
|
126
156
|
renderSelector({ onServiceTierChange: undefined });
|
|
127
157
|
await openPopover("Composer 2.5");
|
|
128
158
|
|
|
@@ -138,35 +168,89 @@ describe("ModelSelector — service tier toggle", () => {
|
|
|
138
168
|
expect(onServiceTierChange).toHaveBeenCalledWith("fast");
|
|
139
169
|
});
|
|
140
170
|
|
|
171
|
+
it("toggling off an active fast tier fires 'standard'", async () => {
|
|
172
|
+
const { onServiceTierChange } = renderSelector({ serviceTier: "fast" });
|
|
173
|
+
await openPopover("Composer 2.5");
|
|
174
|
+
|
|
175
|
+
fireEvent.click(screen.getByRole("switch", { name: "Fast tier" }));
|
|
176
|
+
expect(onServiceTierChange).toHaveBeenCalledOnce();
|
|
177
|
+
expect(onServiceTierChange).toHaveBeenCalledWith("standard");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("keeps the popover open across a toggle", async () => {
|
|
181
|
+
renderSelector();
|
|
182
|
+
await openPopover("Composer 2.5");
|
|
183
|
+
|
|
184
|
+
fireEvent.click(screen.getByRole("switch", { name: "Fast tier" }));
|
|
185
|
+
expect(screen.getByPlaceholderText("Search models…")).toBeTruthy();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe("ModelSelector — trigger badge", () => {
|
|
141
190
|
it("shows the Fast badge on the trigger when the fast tier is active", () => {
|
|
142
191
|
renderSelector({ serviceTier: "fast" });
|
|
143
192
|
|
|
144
193
|
expect(screen.getByText("Fast")).toBeTruthy();
|
|
145
194
|
});
|
|
146
195
|
|
|
147
|
-
it("
|
|
196
|
+
it("hides the badge when the selected model has no fast tier", () => {
|
|
197
|
+
renderSelector({ serviceTier: "fast", value: "claude-haiku-4-5" });
|
|
198
|
+
|
|
199
|
+
expect(screen.queryByText("Fast")).toBeNull();
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe("ModelSelector — fast tier persistence across model switches", () => {
|
|
204
|
+
it("selecting a model while standard emits no tier change", async () => {
|
|
205
|
+
const { onValueChange, onServiceTierChange } = renderSelector();
|
|
206
|
+
await openPopover("Composer 2.5");
|
|
207
|
+
|
|
208
|
+
fireEvent.click(modelOption("Composer 2.5"));
|
|
209
|
+
|
|
210
|
+
expect(onValueChange).toHaveBeenCalledWith("composer-2.5");
|
|
211
|
+
expect(onServiceTierChange).not.toHaveBeenCalled();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("keeps an active fast tier when selecting another fast-capable model", async () => {
|
|
215
|
+
// "Fast, but on that other model" is one action — resetting here made
|
|
216
|
+
// users re-toggle on every model change, and the tier is never silent
|
|
217
|
+
// (trigger badge + options switch both show it).
|
|
148
218
|
const { onValueChange, onServiceTierChange } = renderSelector({
|
|
149
219
|
serviceTier: "fast",
|
|
150
220
|
});
|
|
151
221
|
await openPopover("Composer 2.5");
|
|
152
222
|
|
|
153
|
-
fireEvent.click(modelOption("
|
|
223
|
+
fireEvent.click(modelOption("Claude 4.8 Opus"));
|
|
224
|
+
|
|
225
|
+
expect(onValueChange).toHaveBeenCalledWith("claude-opus-4-8");
|
|
226
|
+
expect(onServiceTierChange).not.toHaveBeenCalled();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("toggle-then-pick works in a single popover visit", async () => {
|
|
230
|
+
// Flip the switch first (popover stays open), then pick a fast-capable
|
|
231
|
+
// model: the toggle survives the selection.
|
|
232
|
+
const { onValueChange, onServiceTierChange } = renderSelector();
|
|
233
|
+
await openPopover("Composer 2.5");
|
|
234
|
+
|
|
235
|
+
fireEvent.click(screen.getByRole("switch", { name: "Fast tier" }));
|
|
236
|
+
fireEvent.click(modelOption("Claude 4.8 Opus"));
|
|
154
237
|
|
|
155
|
-
expect(onValueChange).toHaveBeenCalledOnce();
|
|
156
|
-
expect(onValueChange).toHaveBeenCalledWith("claude-haiku-4-5");
|
|
157
238
|
expect(onServiceTierChange).toHaveBeenCalledOnce();
|
|
158
|
-
expect(onServiceTierChange).toHaveBeenCalledWith("
|
|
239
|
+
expect(onServiceTierChange).toHaveBeenCalledWith("fast");
|
|
240
|
+
expect(onValueChange).toHaveBeenCalledWith("claude-opus-4-8");
|
|
159
241
|
});
|
|
160
242
|
|
|
161
|
-
it("
|
|
162
|
-
const { onServiceTierChange } = renderSelector({
|
|
243
|
+
it("resets an active fast tier when selecting a model without one", async () => {
|
|
244
|
+
const { onValueChange, onServiceTierChange } = renderSelector({
|
|
163
245
|
serviceTier: "fast",
|
|
164
|
-
value: "claude-haiku-4-5",
|
|
165
246
|
});
|
|
166
|
-
await openPopover("
|
|
247
|
+
await openPopover("Composer 2.5");
|
|
167
248
|
|
|
168
|
-
fireEvent.click(modelOption("
|
|
249
|
+
fireEvent.click(modelOption("Haiku 4.5"));
|
|
169
250
|
|
|
170
|
-
expect(
|
|
251
|
+
expect(onValueChange).toHaveBeenCalledOnce();
|
|
252
|
+
expect(onValueChange).toHaveBeenCalledWith("claude-haiku-4-5");
|
|
253
|
+
expect(onServiceTierChange).toHaveBeenCalledOnce();
|
|
254
|
+
expect(onServiceTierChange).toHaveBeenCalledWith("standard");
|
|
171
255
|
});
|
|
172
256
|
});
|
package/src/models/registry.ts
CHANGED
|
@@ -92,8 +92,9 @@ export interface ModelInfo {
|
|
|
92
92
|
/**
|
|
93
93
|
* Pricing-variant keys the registry prices for this model (e.g.
|
|
94
94
|
* `["fast"]`). A priced variant is a *selectable* service tier
|
|
95
|
-
* (stigmer/stigmer#357): the tier
|
|
96
|
-
*
|
|
95
|
+
* (stigmer/stigmer#357): the fast-tier switch in the model popover's
|
|
96
|
+
* options area renders only while the selected model's `serviceTiers`
|
|
97
|
+
* includes it. Empty for models with no variants.
|
|
97
98
|
*
|
|
98
99
|
* Distinct from {@link speedTier}, which is a static latency badge,
|
|
99
100
|
* not a selectable option.
|
|
@@ -11,7 +11,7 @@ import { ServiceTier } from "@stigmer/protos/ai/stigmer/agentic/agentexecution/v
|
|
|
11
11
|
*/
|
|
12
12
|
export type ServiceTierOption = "standard" | "fast";
|
|
13
13
|
|
|
14
|
-
/** The tier key a model must price for the fast
|
|
14
|
+
/** The tier key a model must price for the fast-tier switch to render. */
|
|
15
15
|
export const FAST_SERVICE_TIER = "fast";
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -1206,7 +1206,7 @@ function normalizeRunConfig(
|
|
|
1206
1206
|
if (modelName.trim() !== "") fields.modelName = modelName.trim();
|
|
1207
1207
|
if (maxCostUsd > 0) fields.maxCostUsd = maxCostUsd;
|
|
1208
1208
|
if (maxToolRounds > 0) fields.maxToolRounds = maxToolRounds;
|
|
1209
|
-
// Only an active fast choice is carried — an untouched
|
|
1209
|
+
// Only an active fast choice is carried — an untouched tier stays
|
|
1210
1210
|
// absent, preserving the unspecified-vs-explicit ledger distinction (#357).
|
|
1211
1211
|
if (serviceTier === "fast") fields.serviceTier = toProtoServiceTier(serviceTier);
|
|
1212
1212
|
|
|
@@ -107,11 +107,12 @@ export function ScheduleForm({
|
|
|
107
107
|
// the popover without picking a model pins nothing.
|
|
108
108
|
const [modelName, setModelName] = useState("");
|
|
109
109
|
const [modelHarness, setModelHarness] = useState<HarnessOption>("cursor");
|
|
110
|
-
// Service tier
|
|
111
|
-
// the fast
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
110
|
+
// Service tier is scoped by the model choice (#357): the ModelSelector
|
|
111
|
+
// renders the fast-tier switch only while the selected model's registry
|
|
112
|
+
// entry prices a fast variant. An active fast tier survives switches
|
|
113
|
+
// between fast-capable models and resets only when a model without one
|
|
114
|
+
// is picked. No model pinned = no tier pinned (fast requires a model,
|
|
115
|
+
// and the form's reset button clears both).
|
|
115
116
|
const [serviceTier, setServiceTier] = useState<ServiceTierOption>("standard");
|
|
116
117
|
|
|
117
118
|
const [budgetUsd, setBudgetUsd] = useState("");
|
|
@@ -520,7 +521,7 @@ function buildRunConfig(
|
|
|
520
521
|
if (Number.isFinite(cost) && cost > 0) config.maxCostUsd = cost;
|
|
521
522
|
// Carried only when the user actively chose fast AND pinned a model
|
|
522
523
|
// (fast is a per-model price; the server refuses it without one). An
|
|
523
|
-
// untouched
|
|
524
|
+
// untouched tier stays absent — unspecified-vs-explicit is a
|
|
524
525
|
// load-bearing ledger distinction (#357).
|
|
525
526
|
if (serviceTier === "fast" && model !== "") {
|
|
526
527
|
config.serviceTier = toProtoServiceTier(serviceTier);
|
|
@@ -476,8 +476,8 @@ describe("ScheduleForm", () => {
|
|
|
476
476
|
fireEvent.click(screen.getByRole("button", { name: /Platform default/ }));
|
|
477
477
|
fireEvent.click(await screen.findByRole("option", { name: /Composer 2\.5/ }));
|
|
478
478
|
|
|
479
|
-
// The popover closed on selection; reopen it — the
|
|
480
|
-
// for the now-selected fast-capable model.
|
|
479
|
+
// The popover closed on selection; reopen it — the options-area switch
|
|
480
|
+
// renders only for the now-selected fast-capable model.
|
|
481
481
|
fireEvent.click(screen.getByRole("button", { name: /Composer 2\.5/ }));
|
|
482
482
|
fireEvent.click(await screen.findByRole("switch", { name: "Fast tier" }));
|
|
483
483
|
|
|
@@ -6,6 +6,7 @@ import type { AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v
|
|
|
6
6
|
import type { AgentShareInput } from "@stigmer/sdk";
|
|
7
7
|
import { StigmerContext } from "../../context";
|
|
8
8
|
import {
|
|
9
|
+
draftFromShare,
|
|
9
10
|
sharingAudienceFromProto,
|
|
10
11
|
useSaveAgentShare,
|
|
11
12
|
type AgentShareDraft,
|
|
@@ -50,6 +51,7 @@ const FULL_DRAFT: AgentShareDraft = {
|
|
|
50
51
|
conversationEnded: "This conversation has ended.",
|
|
51
52
|
},
|
|
52
53
|
environmentRefs: [{ org: "acme", slug: "github-org-shared" }],
|
|
54
|
+
runConfig: { modelName: "gpt-5-mini", maxCostUsd: 0.25 },
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
describe("useSaveAgentShare", () => {
|
|
@@ -88,6 +90,7 @@ describe("useSaveAgentShare", () => {
|
|
|
88
90
|
expect(input.environmentRefs).toEqual([
|
|
89
91
|
{ org: "acme", slug: "github-org-shared" },
|
|
90
92
|
]);
|
|
93
|
+
expect(input.runConfig).toEqual({ modelName: "gpt-5-mini", maxCostUsd: 0.25 });
|
|
91
94
|
});
|
|
92
95
|
|
|
93
96
|
it("keeps the existing share's identity when editing (a renamed share is never forked)", async () => {
|
|
@@ -123,7 +126,15 @@ describe("useSaveAgentShare", () => {
|
|
|
123
126
|
|
|
124
127
|
await act(() =>
|
|
125
128
|
result.current.save(
|
|
126
|
-
|
|
129
|
+
// An org-audience share carries no environmentRefs or runConfig
|
|
130
|
+
// (both are public-audience-only by the proto CEL rules).
|
|
131
|
+
{
|
|
132
|
+
...FULL_DRAFT,
|
|
133
|
+
enabled: false,
|
|
134
|
+
audience: "org",
|
|
135
|
+
environmentRefs: [],
|
|
136
|
+
runConfig: undefined,
|
|
137
|
+
},
|
|
127
138
|
null,
|
|
128
139
|
),
|
|
129
140
|
);
|
|
@@ -138,6 +149,49 @@ describe("useSaveAgentShare", () => {
|
|
|
138
149
|
expect(input.audience).toBe(AgentShareAudience.org);
|
|
139
150
|
});
|
|
140
151
|
|
|
152
|
+
it("a pause/resume toggle built from draftFromShare carries run_config (never wipes an API-set override)", async () => {
|
|
153
|
+
const apply = vi.fn().mockResolvedValue({});
|
|
154
|
+
const client = createMockStigmer({ apply });
|
|
155
|
+
// A share whose run_config was set via CLI/API — the console list's
|
|
156
|
+
// toggle rebuilds the full spec from draftFromShare, so the override
|
|
157
|
+
// must ride through even though no console surface edits it.
|
|
158
|
+
const share = {
|
|
159
|
+
metadata: { id: "ash_1", org: "acme", slug: "support-agent", name: "Support Agent" },
|
|
160
|
+
spec: {
|
|
161
|
+
enabled: true,
|
|
162
|
+
allowedOrigins: ["https://example.com"],
|
|
163
|
+
environmentRefs: [],
|
|
164
|
+
runConfig: { modelName: "gpt-5-mini", maxCostUsd: 0.25, maxToolRounds: 8 },
|
|
165
|
+
},
|
|
166
|
+
} as unknown as AgentShare;
|
|
167
|
+
|
|
168
|
+
const { result } = renderHook(() => useSaveAgentShare(AGENT), {
|
|
169
|
+
wrapper: wrapper(client),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
await act(() =>
|
|
173
|
+
result.current.save({ ...draftFromShare(share), enabled: false }, share),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const input = apply.mock.calls[0][0] as AgentShareInput;
|
|
177
|
+
expect(input.enabled).toBe(false);
|
|
178
|
+
expect(input.runConfig).toEqual({
|
|
179
|
+
modelName: "gpt-5-mini",
|
|
180
|
+
maxCostUsd: 0.25,
|
|
181
|
+
maxToolRounds: 8,
|
|
182
|
+
serviceTier: undefined,
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it("draftFromShare leaves run_config undefined when the share carries none", () => {
|
|
187
|
+
expect(draftFromShare(null).runConfig).toBeUndefined();
|
|
188
|
+
const bare = {
|
|
189
|
+
metadata: { id: "ash_2", org: "acme", slug: "bare" },
|
|
190
|
+
spec: { enabled: true },
|
|
191
|
+
} as AgentShare;
|
|
192
|
+
expect(draftFromShare(bare).runConfig).toBeUndefined();
|
|
193
|
+
});
|
|
194
|
+
|
|
141
195
|
it("maps the audience union to the proto enum, writing public explicitly", async () => {
|
|
142
196
|
const apply = vi.fn().mockResolvedValue({});
|
|
143
197
|
const client = createMockStigmer({ apply });
|
|
@@ -4,7 +4,7 @@ import { useCallback, useState } from "react";
|
|
|
4
4
|
import type { Agent } from "@stigmer/protos/ai/stigmer/agentic/agent/v1/api_pb";
|
|
5
5
|
import type { AgentShare } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/api_pb";
|
|
6
6
|
import { AgentShareAudience } from "@stigmer/protos/ai/stigmer/agentic/agentshare/v1/spec_pb";
|
|
7
|
-
import type { ResourceRef } from "@stigmer/sdk";
|
|
7
|
+
import type { ResourceRef, RunConfigInput } from "@stigmer/sdk";
|
|
8
8
|
import { useStigmer } from "../hooks.js";
|
|
9
9
|
import { toError } from "../internal/toError.js";
|
|
10
10
|
|
|
@@ -57,6 +57,15 @@ export interface AgentShareDraft {
|
|
|
57
57
|
* sessions carry no share linkage in Phase A — decision 011 addendum).
|
|
58
58
|
*/
|
|
59
59
|
readonly environmentRefs: readonly ResourceRef[];
|
|
60
|
+
/**
|
|
61
|
+
* The owner's per-share execution override (model / cost cap / tool
|
|
62
|
+
* rounds / service tier), merged over the platform guest profile at
|
|
63
|
+
* run time — stigmer/stigmer#360. The console does not edit it (yet):
|
|
64
|
+
* it rides through every save opaquely so a toggle never wipes an
|
|
65
|
+
* override set via the CLI or API. Public-audience only (the same
|
|
66
|
+
* proto CEL rule as environmentRefs, for the same reason).
|
|
67
|
+
*/
|
|
68
|
+
readonly runConfig: RunConfigInput | undefined;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
/**
|
|
@@ -103,6 +112,14 @@ export function draftFromShare(share: AgentShare | null): AgentShareDraft {
|
|
|
103
112
|
org: ref.org,
|
|
104
113
|
slug: ref.slug,
|
|
105
114
|
})),
|
|
115
|
+
runConfig: spec?.runConfig
|
|
116
|
+
? {
|
|
117
|
+
modelName: spec.runConfig.modelName,
|
|
118
|
+
maxCostUsd: spec.runConfig.maxCostUsd,
|
|
119
|
+
maxToolRounds: spec.runConfig.maxToolRounds,
|
|
120
|
+
serviceTier: spec.runConfig.serviceTier,
|
|
121
|
+
}
|
|
122
|
+
: undefined,
|
|
106
123
|
};
|
|
107
124
|
}
|
|
108
125
|
|
|
@@ -229,6 +246,7 @@ export function useSaveAgentShare(
|
|
|
229
246
|
org: ref.org,
|
|
230
247
|
slug: ref.slug,
|
|
231
248
|
})),
|
|
249
|
+
runConfig: draft.runConfig,
|
|
232
250
|
});
|
|
233
251
|
} catch (err) {
|
|
234
252
|
setError(toError(err));
|