@shipfox/client-agent 14.0.0 → 15.0.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.
Files changed (55) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +22 -0
  3. package/dist/components/add-custom-provider-card.js +3 -3
  4. package/dist/components/add-custom-provider-card.js.map +1 -1
  5. package/dist/components/available-provider-card.js +3 -3
  6. package/dist/components/available-provider-card.js.map +1 -1
  7. package/dist/components/available-providers-grid.d.ts +1 -1
  8. package/dist/components/available-providers-grid.d.ts.map +1 -1
  9. package/dist/components/available-providers-grid.js +2 -2
  10. package/dist/components/available-providers-grid.js.map +1 -1
  11. package/dist/components/change-default-model-form.js +3 -3
  12. package/dist/components/change-default-model-form.js.map +1 -1
  13. package/dist/components/custom-model-provider-form.js +18 -19
  14. package/dist/components/custom-model-provider-form.js.map +1 -1
  15. package/dist/components/custom-model-provider-form.stories.d.ts.map +1 -1
  16. package/dist/components/default-model-field.js +1 -1
  17. package/dist/components/default-model-field.js.map +1 -1
  18. package/dist/components/harnesses-section.js +7 -7
  19. package/dist/components/harnesses-section.js.map +1 -1
  20. package/dist/components/harnesses-section.stories.d.ts.map +1 -1
  21. package/dist/components/model-provider-usage-modal.js +8 -8
  22. package/dist/components/model-provider-usage-modal.js.map +1 -1
  23. package/dist/components/model-provider-usage-modal.stories.d.ts.map +1 -1
  24. package/dist/components/model-providers-section.js +20 -20
  25. package/dist/components/model-providers-section.js.map +1 -1
  26. package/dist/components/model-providers-section.stories.d.ts.map +1 -1
  27. package/dist/components/test-and-save-form.js +3 -3
  28. package/dist/components/test-and-save-form.js.map +1 -1
  29. package/dist/feature.d.ts +8 -8
  30. package/dist/feature.d.ts.map +1 -1
  31. package/dist/hooks/api/model-providers.d.ts +1 -1
  32. package/dist/hooks/api/model-providers.d.ts.map +1 -1
  33. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
  34. package/dist/pages/model-provider-onboarding-page.js +51 -43
  35. package/dist/pages/model-provider-onboarding-page.js.map +1 -1
  36. package/dist/pages/model-provider-onboarding-page.stories.d.ts.map +1 -1
  37. package/dist/routes/agents-settings.d.ts.map +1 -1
  38. package/dist/routes/agents-settings.js +1 -1
  39. package/dist/routes/agents-settings.js.map +1 -1
  40. package/dist/routes/model-provider.d.ts.map +1 -1
  41. package/dist/tsconfig.test.tsbuildinfo +1 -1
  42. package/package.json +4 -4
  43. package/src/components/add-custom-provider-card.tsx +3 -3
  44. package/src/components/available-provider-card.tsx +3 -3
  45. package/src/components/available-providers-grid.tsx +2 -2
  46. package/src/components/change-default-model-form.tsx +3 -3
  47. package/src/components/custom-model-provider-form.tsx +23 -20
  48. package/src/components/default-model-field.tsx +1 -1
  49. package/src/components/harnesses-section.tsx +7 -7
  50. package/src/components/model-provider-usage-modal.tsx +8 -8
  51. package/src/components/model-providers-section.tsx +20 -20
  52. package/src/components/test-and-save-form.tsx +3 -3
  53. package/src/pages/model-provider-onboarding-page.tsx +38 -34
  54. package/src/routes/agents-settings.tsx +1 -1
  55. package/tsconfig.build.tsbuildinfo +1 -1
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/client-agent",
3
3
  "license": "MIT",
4
- "version": "14.0.0",
4
+ "version": "15.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -30,9 +30,9 @@
30
30
  "@tanstack/react-query": "^5.101.0",
31
31
  "@shipfox/api-agent-dto": "12.0.0",
32
32
  "@shipfox/client-api": "6.0.1",
33
- "@shipfox/client-shell": "14.0.0",
34
- "@shipfox/client-ui": "14.0.0",
35
- "@shipfox/react-ui": "0.5.0"
33
+ "@shipfox/client-ui": "14.0.1",
34
+ "@shipfox/react-ui": "1.0.0",
35
+ "@shipfox/client-shell": "14.0.1"
36
36
  },
37
37
  "peerDependencies": {
38
38
  "@tanstack/react-query": "^5.101.0",
@@ -11,17 +11,17 @@ export function AddCustomProviderCard({onConfigure}: {onConfigure: () => void})
11
11
  <button
12
12
  type="button"
13
13
  className={cn(
14
- 'group block h-full w-full cursor-pointer p-16 text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
14
+ 'group block h-full w-full cursor-pointer p-panel-compact text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
15
15
  SURFACE_CLASS,
16
16
  )}
17
17
  aria-label="Configure custom provider"
18
18
  onClick={onConfigure}
19
19
  >
20
- <div className="flex min-w-0 items-center justify-between gap-12">
20
+ <div className="flex min-w-0 items-center justify-between gap-cluster">
21
21
  <Text size="md" bold className="min-w-0 truncate">
22
22
  Custom
23
23
  </Text>
24
- <div className="flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
24
+ <div className="flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
25
25
  <Text size="sm">Configure</Text>
26
26
  <Icon name="chevronRight" className="size-16" />
27
27
  </div>
@@ -18,17 +18,17 @@ export function AvailableProviderCard({
18
18
  <button
19
19
  type="button"
20
20
  className={cn(
21
- 'group block w-full cursor-pointer p-16 text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
21
+ 'group block w-full cursor-pointer p-panel-compact text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
22
22
  SURFACE_CLASS,
23
23
  )}
24
24
  aria-label={`Configure ${entry.label}`}
25
25
  onClick={onConfigure}
26
26
  >
27
- <div className="flex min-w-0 items-center justify-between gap-12">
27
+ <div className="flex min-w-0 items-center justify-between gap-cluster">
28
28
  <Text size="md" bold className="min-w-0 truncate">
29
29
  {entry.label}
30
30
  </Text>
31
- <div className="flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
31
+ <div className="flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
32
32
  <Text size="sm">Configure</Text>
33
33
  <Icon name="chevronRight" className="size-16" />
34
34
  </div>
@@ -8,7 +8,7 @@ import type {SupportedProvider} from '#core/models.js';
8
8
  import {providerMatchesSearch} from '#core/provider-policy.js';
9
9
  import {AvailableProviderCard} from './available-provider-card.js';
10
10
 
11
- export const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-12 max-[760px]:grid-cols-1';
11
+ export const PROVIDER_GRID_CLASS = 'grid grid-cols-2 gap-cluster max-[760px]:grid-cols-1';
12
12
 
13
13
  const SEARCH_VISIBILITY_THRESHOLD = 8;
14
14
  const MAX_ECHOED_QUERY_LENGTH = 40;
@@ -45,7 +45,7 @@ export function AvailableProvidersGrid<TEntry extends SupportedProvider>({
45
45
  }
46
46
 
47
47
  return (
48
- <div className="flex flex-col gap-12">
48
+ <div className="flex flex-col gap-cluster">
49
49
  {showSearch ? (
50
50
  <Input
51
51
  ref={inputRef}
@@ -49,10 +49,10 @@ export function ChangeDefaultModelForm({
49
49
 
50
50
  return (
51
51
  <>
52
- <ModalBody className="gap-16">
52
+ <ModalBody className="gap-group">
53
53
  <form
54
54
  id={CHANGE_DEFAULT_MODEL_FORM_ID}
55
- className="flex w-full flex-col gap-14"
55
+ className="flex w-full flex-col gap-cluster"
56
56
  noValidate
57
57
  onSubmit={(event) => {
58
58
  event.preventDefault();
@@ -80,7 +80,7 @@ export function ChangeDefaultModelForm({
80
80
  </form>
81
81
  {formError ? (
82
82
  <Callout role="alert" type="error">
83
- <div className="flex flex-col gap-8">
83
+ <div className="flex flex-col gap-inline">
84
84
  <Text size="sm" bold>
85
85
  Could not save default model
86
86
  </Text>
@@ -151,10 +151,10 @@ export function CustomModelProviderForm({
151
151
 
152
152
  return (
153
153
  <>
154
- <ModalBody className="min-h-0 flex-1 gap-0 overflow-y-auto overflow-x-clip scrollbar">
154
+ <ModalBody className="min-h-0 flex-1 gap-group overflow-y-auto overflow-x-clip scrollbar">
155
155
  <form
156
156
  id={CUSTOM_MODEL_PROVIDER_FORM_ID}
157
- className="flex w-full flex-col gap-24"
157
+ className="flex w-full flex-col gap-section"
158
158
  noValidate
159
159
  onSubmit={(event) => {
160
160
  event.preventDefault();
@@ -166,7 +166,7 @@ export function CustomModelProviderForm({
166
166
  {(values) => (
167
167
  <>
168
168
  <FormGroup title="Provider">
169
- <div className="grid grid-cols-2 gap-12 max-[640px]:grid-cols-1">
169
+ <div className="grid grid-cols-2 gap-cluster max-[640px]:grid-cols-1">
170
170
  <form.Field
171
171
  name="display_name"
172
172
  validators={{
@@ -234,7 +234,7 @@ export function CustomModelProviderForm({
234
234
  </form.Field>
235
235
  </div>
236
236
 
237
- <div className="grid grid-cols-2 gap-12 max-[640px]:grid-cols-1">
237
+ <div className="grid grid-cols-2 gap-cluster max-[640px]:grid-cols-1">
238
238
  <form.Field name="api">
239
239
  {(field) => (
240
240
  <FormField label="Protocol" id="custom-provider-api">
@@ -320,7 +320,7 @@ export function CustomModelProviderForm({
320
320
  </FormGroup>
321
321
 
322
322
  <FormGroup title="Models">
323
- <div className="flex flex-wrap items-center justify-between gap-8">
323
+ <div className="flex flex-wrap items-center justify-between gap-inline">
324
324
  <Button
325
325
  type="button"
326
326
  size="sm"
@@ -417,8 +417,8 @@ export function CustomModelProviderForm({
417
417
  </form.Subscribe>
418
418
  </form>
419
419
  {formError ? (
420
- <Callout role="alert" type="error" className="mt-16">
421
- <div className="flex flex-col gap-8">
420
+ <Callout role="alert" type="error">
421
+ <div className="flex flex-col gap-inline">
422
422
  <Text size="sm" bold>
423
423
  Could not save provider
424
424
  </Text>
@@ -441,7 +441,7 @@ export function CustomModelProviderForm({
441
441
 
442
442
  function FormGroup({title, children}: {title: string; children: ReactNode}) {
443
443
  return (
444
- <section className="flex flex-col gap-16 border-t border-border-neutral-base pt-16 first:border-t-0 first:pt-0">
444
+ <section className="flex flex-col gap-group border-t border-border-neutral-base pt-[16px] first:border-t-0 first:pt-0">
445
445
  <Text size="xs" className="font-medium uppercase text-foreground-neutral-muted">
446
446
  {title}
447
447
  </Text>
@@ -460,11 +460,11 @@ function HeaderRows({
460
460
  const addButtonRef = useRef<HTMLButtonElement>(null);
461
461
 
462
462
  return (
463
- <div className="flex flex-col gap-10">
463
+ <div className="flex flex-col gap-inline">
464
464
  {rows.map((row, index) => (
465
465
  <div
466
466
  key={row.client_id}
467
- className="grid grid-cols-[1fr_1fr_auto_auto] items-end gap-x-8 gap-y-4 max-[760px]:grid-cols-1"
467
+ className="grid grid-cols-[1fr_1fr_auto_auto] items-end gap-x-inline gap-y-tight max-[760px]:grid-cols-1"
468
468
  >
469
469
  <FormField label="Name" id={`custom-header-name-${row.client_id}`}>
470
470
  <FormFieldInput
@@ -485,7 +485,7 @@ function HeaderRows({
485
485
  }
486
486
  />
487
487
  </FormField>
488
- <div className="flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted">
488
+ <div className="flex h-32 items-center gap-inline text-sm text-foreground-neutral-muted">
489
489
  <Switch
490
490
  size="sm"
491
491
  aria-label={`Mark header ${index + 1} as secret`}
@@ -553,10 +553,13 @@ function ModelRows({
553
553
  const addButtonRef = useRef<HTMLButtonElement>(null);
554
554
 
555
555
  return (
556
- <div className="flex flex-col gap-10">
556
+ <div className="flex flex-col gap-inline">
557
557
  {rows.map((row, index) => (
558
- <div key={row.client_id} className="rounded-8 border border-border-neutral-base p-12">
559
- <div className="grid grid-cols-[1fr_1fr_auto] items-end gap-8 max-[760px]:grid-cols-1">
558
+ <div
559
+ key={row.client_id}
560
+ className="flex flex-col gap-inline rounded-8 border border-border-neutral-base p-panel-compact"
561
+ >
562
+ <div className="grid grid-cols-[1fr_1fr_auto] items-end gap-inline max-[760px]:grid-cols-1">
560
563
  <FormField label="Model id" id={`custom-model-id-${row.client_id}`}>
561
564
  <FormFieldInput
562
565
  className="font-code"
@@ -574,7 +577,7 @@ function ModelRows({
574
577
  }
575
578
  />
576
579
  </FormField>
577
- <div className="flex items-center gap-8">
580
+ <div className="flex items-center gap-inline">
578
581
  {defaultModel && row.id === defaultModel ? (
579
582
  <Badge variant="neutral">Default</Badge>
580
583
  ) : null}
@@ -596,14 +599,14 @@ function ModelRows({
596
599
  />
597
600
  </div>
598
601
  </div>
599
- <Collapsible>
602
+ <Collapsible className="flex flex-col">
600
603
  <CollapsibleTrigger asChild>
601
- <Button type="button" size="sm" variant="transparentMuted" className="mt-8">
604
+ <Button type="button" size="sm" variant="transparentMuted">
602
605
  Defaults: 128k context, 16k output, text-only
603
606
  </Button>
604
607
  </CollapsibleTrigger>
605
608
  <CollapsibleContent>
606
- <div className="mt-8 grid grid-cols-2 gap-8 max-[640px]:grid-cols-1">
609
+ <div className="grid grid-cols-2 gap-inline pt-[8px] max-[640px]:grid-cols-1">
607
610
  <FormField label="Context window" id={`custom-model-context-${row.client_id}`}>
608
611
  <FormFieldInput
609
612
  type="number"
@@ -631,7 +634,7 @@ function ModelRows({
631
634
  }
632
635
  />
633
636
  </FormField>
634
- <div className="flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted">
637
+ <div className="flex h-32 items-center gap-inline text-sm text-foreground-neutral-muted">
635
638
  <Switch
636
639
  size="sm"
637
640
  aria-label={`Enable image input for model ${index + 1}`}
@@ -642,7 +645,7 @@ function ModelRows({
642
645
  />
643
646
  Image input
644
647
  </div>
645
- <div className="flex h-32 items-center gap-8 text-sm text-foreground-neutral-muted">
648
+ <div className="flex h-32 items-center gap-inline text-sm text-foreground-neutral-muted">
646
649
  <Switch
647
650
  size="sm"
648
651
  aria-label={`Enable reasoning for model ${index + 1}`}
@@ -73,7 +73,7 @@ export function FormFieldSelect({className, ...props}: ComponentProps<'select'>)
73
73
  return (
74
74
  <select
75
75
  className={cn(
76
- 'w-full min-w-0 rounded-6 bg-background-field-base px-8 py-6 text-sm leading-20 text-foreground-neutral-base shadow-button-neutral outline-none transition-[color,box-shadow]',
76
+ 'w-full min-w-0 rounded-6 bg-background-field-base px-tight py-[6px] text-sm leading-20 text-foreground-neutral-base shadow-button-neutral outline-none transition-[color,box-shadow]',
77
77
  'hover:bg-background-field-hover focus-visible:shadow-border-interactive-with-active disabled:cursor-not-allowed disabled:bg-background-neutral-disabled disabled:text-foreground-neutral-disabled',
78
78
  'aria-invalid:shadow-border-error',
79
79
  className,
@@ -84,8 +84,8 @@ export function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string})
84
84
  }
85
85
 
86
86
  return (
87
- <section className="flex flex-col gap-16" aria-label="Harnesses">
88
- <div className="flex flex-col gap-4">
87
+ <section className="flex flex-col gap-group" aria-label="Harnesses">
88
+ <div className="flex flex-col gap-tight">
89
89
  <Header variant="h3">Harnesses</Header>
90
90
  <Text size="sm" className="text-foreground-neutral-muted">
91
91
  Harnesses available to run agent steps in this workspace.
@@ -95,7 +95,7 @@ export function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string})
95
95
  {configsQuery.isPending ? <HarnessRowsSkeleton /> : null}
96
96
 
97
97
  {configsQuery.isError && configsQuery.data === undefined ? (
98
- <div className={cn(SURFACE_CLASS, 'px-16')}>
98
+ <div className={cn(SURFACE_CLASS, 'px-row')}>
99
99
  <QueryLoadError query={configsQuery} subject="harnesses" />
100
100
  </div>
101
101
  ) : null}
@@ -141,9 +141,9 @@ function HarnessRow({
141
141
  const unavailableCopy = harnessUnavailableCopy(isDefault);
142
142
 
143
143
  return (
144
- <li className="flex flex-col gap-10 px-16 py-12 transition-colors hover:bg-background-components-hover">
145
- <div className="flex items-center justify-between gap-12">
146
- <div className="flex min-w-0 items-center gap-8">
144
+ <li className="flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover">
145
+ <div className="flex items-center justify-between gap-cluster">
146
+ <div className="flex min-w-0 items-center gap-inline">
147
147
  {isDefault ? (
148
148
  <>
149
149
  <Tooltip>
@@ -230,7 +230,7 @@ function HarnessRowsSkeleton() {
230
230
  className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}
231
231
  >
232
232
  {[0, 1].map((row) => (
233
- <li key={row} className="flex items-center gap-12 px-16 py-12">
233
+ <li key={row} className="flex items-center gap-cluster px-row py-row">
234
234
  <Skeleton className="size-16 shrink-0" />
235
235
  <Skeleton className="h-16 w-120" />
236
236
  <Skeleton className="ml-auto size-28 shrink-0" />
@@ -109,7 +109,7 @@ export function ModelProviderUsageModal({
109
109
  {target ? `Use ${target.label} in a workflow` : 'Use provider in a workflow'}
110
110
  </ModalTitle>
111
111
  <ModalHeader>
112
- <div className="flex min-w-0 flex-col gap-2">
112
+ <div className="flex min-w-0 flex-col gap-tight">
113
113
  <Text size="lg" aria-hidden="true" className="truncate">
114
114
  {target ? `Use ${target.label} in a workflow` : 'Use provider in a workflow'}
115
115
  </Text>
@@ -121,10 +121,10 @@ export function ModelProviderUsageModal({
121
121
  {target && example ? (
122
122
  <>
123
123
  <ModalBody className="min-h-0 flex-1 gap-0 overflow-y-auto overflow-x-clip scrollbar">
124
- <div className="flex w-full flex-col gap-20">
124
+ <div className="flex w-full flex-col gap-section">
125
125
  {compatibleIds.length === 1 ? (
126
- <div className="flex flex-col gap-12">
127
- <div className="flex min-h-40 items-center justify-between gap-12 rounded-8 border border-border-neutral-base px-12 py-8">
126
+ <div className="flex flex-col gap-cluster">
127
+ <div className="flex min-h-40 items-center justify-between gap-cluster rounded-8 border border-border-neutral-base px-row py-row">
128
128
  <Text size="sm" className="text-foreground-neutral-muted">
129
129
  Harness
130
130
  </Text>
@@ -146,7 +146,7 @@ export function ModelProviderUsageModal({
146
146
  />
147
147
  </div>
148
148
  ) : (
149
- <div className="flex flex-col gap-12 sm:flex-row sm:gap-12">
149
+ <div className="flex flex-col gap-cluster sm:flex-row sm:gap-cluster">
150
150
  <Combobox
151
151
  aria-label="Harness"
152
152
  options={harnessOptions}
@@ -174,7 +174,7 @@ export function ModelProviderUsageModal({
174
174
  </div>
175
175
  )}
176
176
 
177
- <div className="flex flex-col gap-8">
177
+ <div className="flex flex-col gap-inline">
178
178
  <CodeBlock data={data} className="h-auto min-h-0 rounded-8">
179
179
  <CodeBlockHeader>
180
180
  <CodeBlockFiles>
@@ -210,7 +210,7 @@ export function ModelProviderUsageModal({
210
210
  </Text>
211
211
  </div>
212
212
 
213
- <div className="flex flex-col gap-8">
213
+ <div className="flex flex-col gap-inline">
214
214
  <Text size="sm" bold>
215
215
  Available models ({target.models.length})
216
216
  </Text>
@@ -286,7 +286,7 @@ function ModelProviderModelRow({label, id}: {label: string; id: string}) {
286
286
  <button
287
287
  type="button"
288
288
  aria-label={`Copy ${label} model id ${id}`}
289
- className="flex min-h-40 w-full min-w-0 flex-col items-start gap-2 px-12 py-8 text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none sm:flex-row sm:items-center sm:gap-8"
289
+ className="flex min-h-40 w-full min-w-0 flex-col items-start gap-tight px-row py-row text-left transition-colors hover:bg-background-components-hover focus-visible:shadow-border-interactive-with-active focus-visible:outline-none sm:flex-row sm:items-center sm:gap-inline"
290
290
  onClick={() => {
291
291
  void handleCopy();
292
292
  }}
@@ -113,14 +113,14 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
113
113
  }, [configs, modal, providerById]);
114
114
 
115
115
  return (
116
- <div className="flex min-w-0 flex-col gap-32">
116
+ <div className="flex min-w-0 flex-col gap-region">
117
117
  <section
118
118
  ref={configuredProvidersRegionRef}
119
- className="flex flex-col gap-16 outline-none"
119
+ className="flex flex-col gap-group outline-none"
120
120
  aria-label="Configured providers"
121
121
  tabIndex={-1}
122
122
  >
123
- <div className="flex flex-col gap-4">
123
+ <div className="flex flex-col gap-tight">
124
124
  <Header variant="h3">Configured providers</Header>
125
125
  <Text size="sm" className="text-foreground-neutral-muted">
126
126
  Workspace credentials available to agent steps.
@@ -132,13 +132,13 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
132
132
  ) : null}
133
133
 
134
134
  {configsQuery.isError && configsQuery.data === undefined ? (
135
- <div className={cn(SURFACE_CLASS, 'px-16')}>
135
+ <div className={cn(SURFACE_CLASS, 'px-row')}>
136
136
  <QueryLoadError query={configsQuery} subject="model provider configs" />
137
137
  </div>
138
138
  ) : null}
139
139
 
140
140
  {configsQuery.data !== undefined && configs.length === 0 ? (
141
- <div className={cn(SURFACE_CLASS, 'px-16')}>
141
+ <div className={cn(SURFACE_CLASS, 'px-row')}>
142
142
  <EmptyState
143
143
  icon="key2Line"
144
144
  title="No providers configured"
@@ -203,8 +203,8 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
203
203
  ) : null}
204
204
  </section>
205
205
 
206
- <section className="flex flex-col gap-16" aria-label="Available providers">
207
- <div className="flex flex-col gap-4">
206
+ <section className="flex flex-col gap-group" aria-label="Available providers">
207
+ <div className="flex flex-col gap-tight">
208
208
  <Header variant="h3">Available providers</Header>
209
209
  <Text size="sm" className="text-foreground-neutral-muted">
210
210
  Providers that can be configured for agent steps in this workspace.
@@ -214,7 +214,7 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
214
214
  {catalogQuery.isPending || configsQuery.isPending ? <ModelProviderGridSkeleton /> : null}
215
215
 
216
216
  {catalogQuery.isError && catalogQuery.data === undefined ? (
217
- <div className={cn(SURFACE_CLASS, 'px-16')}>
217
+ <div className={cn(SURFACE_CLASS, 'px-row')}>
218
218
  <QueryLoadError query={catalogQuery} subject="model provider catalog" />
219
219
  </div>
220
220
  ) : null}
@@ -231,8 +231,8 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
231
231
  ) : null}
232
232
  </section>
233
233
 
234
- <section className="flex flex-col gap-16" aria-label="Unsupported providers">
235
- <div className="flex flex-col gap-4">
234
+ <section className="flex flex-col gap-group" aria-label="Unsupported providers">
235
+ <div className="flex flex-col gap-tight">
236
236
  <Header variant="h3">Unsupported providers</Header>
237
237
  <Text size="sm" className="text-foreground-neutral-muted">
238
238
  Providers that cannot be configured in this workspace yet.
@@ -246,13 +246,13 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
246
246
  {unsupportedProviders.length > 0 ? (
247
247
  <ul className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}>
248
248
  {unsupportedProviders.map((entry) => (
249
- <li key={entry.id} className="flex items-start gap-12 px-16 py-12 opacity-70">
249
+ <li key={entry.id} className="flex items-start gap-cluster px-row py-row opacity-70">
250
250
  <Icon
251
251
  name="forbid2Line"
252
- className="mt-2 size-18 shrink-0 text-foreground-neutral-muted"
252
+ className="mt-[2px] size-18 shrink-0 text-foreground-neutral-muted"
253
253
  aria-hidden
254
254
  />
255
- <div className="flex min-w-0 flex-1 flex-col gap-2">
255
+ <div className="flex min-w-0 flex-1 flex-col gap-tight">
256
256
  <Text size="md" bold className="truncate">
257
257
  {entry.label}
258
258
  </Text>
@@ -357,7 +357,7 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
357
357
  >
358
358
  <ModalTitle className="sr-only">{customModelProviderFormTitle(modal)}</ModalTitle>
359
359
  <ModalHeader>
360
- <div className="flex min-w-0 flex-col gap-2">
360
+ <div className="flex min-w-0 flex-col gap-tight">
361
361
  <Text size="lg" aria-hidden="true" className="truncate">
362
362
  {customModelProviderFormTitle(modal)}
363
363
  </Text>
@@ -468,9 +468,9 @@ function ConfiguredProviderRow({
468
468
  }
469
469
 
470
470
  return (
471
- <li className="flex flex-col gap-10 px-16 py-12 transition-colors hover:bg-background-components-hover">
472
- <div className="flex items-center justify-between gap-12">
473
- <div className="flex min-w-0 items-center gap-8">
471
+ <li className="flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover">
472
+ <div className="flex items-center justify-between gap-cluster">
473
+ <div className="flex min-w-0 items-center gap-inline">
474
474
  {isDefault ? (
475
475
  <>
476
476
  <Tooltip>
@@ -576,7 +576,7 @@ function DeleteModelProviderDialog({
576
576
  <ModalContent aria-describedby={undefined} className="max-w-[420px]">
577
577
  <ModalTitle className="sr-only">Delete model provider</ModalTitle>
578
578
  <ModalHeader title="Delete model provider" />
579
- <ModalBody className="gap-16">
579
+ <ModalBody className="gap-group">
580
580
  <Text size="sm" className="text-foreground-neutral-muted">
581
581
  Delete {label} credentials from this workspace? Agent jobs cannot use this provider
582
582
  until it is configured again.
@@ -608,9 +608,9 @@ function ModelProviderRowsSkeleton({label}: {label: string}) {
608
608
  className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}
609
609
  >
610
610
  {[0, 1, 2].map((row) => (
611
- <li key={row} className="flex items-center gap-12 px-16 py-12">
611
+ <li key={row} className="flex items-center gap-cluster px-row py-row">
612
612
  <Skeleton className="size-32 shrink-0" />
613
- <div className="flex min-w-0 flex-1 flex-col gap-6">
613
+ <div className="flex min-w-0 flex-1 flex-col gap-inline">
614
614
  <Skeleton className="h-16 w-120" />
615
615
  <Skeleton className="h-14 w-180" />
616
616
  </div>
@@ -68,10 +68,10 @@ export function ModelProviderTestAndSaveForm({
68
68
 
69
69
  return (
70
70
  <>
71
- <ModalBody className="gap-16">
71
+ <ModalBody className="gap-group">
72
72
  <form
73
73
  id={MODEL_PROVIDER_TEST_AND_SAVE_FORM_ID}
74
- className="flex w-full flex-col gap-14"
74
+ className="flex w-full flex-col gap-cluster"
75
75
  noValidate
76
76
  onSubmit={(event) => {
77
77
  event.preventDefault();
@@ -129,7 +129,7 @@ export function ModelProviderTestAndSaveForm({
129
129
  </form>
130
130
  {formError ? (
131
131
  <Callout role="alert" type="error">
132
- <div className="flex flex-col gap-8">
132
+ <div className="flex flex-col gap-inline">
133
133
  <Text size="sm" bold>
134
134
  Could not save provider
135
135
  </Text>
@@ -104,9 +104,9 @@ export function ModelProviderOnboardingPage({
104
104
  : 'model-provider-provider-step';
105
105
 
106
106
  return (
107
- <div className="mx-auto flex w-full max-w-[640px] flex-col gap-20">
108
- <header className="flex flex-col gap-12">
109
- <div className="flex items-start justify-between gap-16 max-[520px]:flex-col max-[520px]:gap-8">
107
+ <div className="mx-auto flex w-full max-w-[640px] flex-col gap-section">
108
+ <header className="flex flex-col gap-cluster">
109
+ <div className="flex items-start justify-between gap-group max-[520px]:flex-col max-[520px]:gap-inline">
110
110
  <Header id={headingId} variant="h1" tabIndex={-1} className="outline-none">
111
111
  {selectedHarnessDescriptor
112
112
  ? `Configure ${selectedHarnessDescriptor.label}`
@@ -123,7 +123,7 @@ export function ModelProviderOnboardingPage({
123
123
  </Text>
124
124
  </header>
125
125
 
126
- <section aria-labelledby={headingId} className="flex flex-col gap-16">
126
+ <section aria-labelledby={headingId} className="flex flex-col gap-group">
127
127
  {onboarding.step === 'choose-harness' ? (
128
128
  <HarnessPicker
129
129
  onSelect={(harnessId) => dispatch({type: 'harness-selected', harnessId})}
@@ -174,36 +174,40 @@ export function ModelProviderOnboardingPage({
174
174
  : ''}
175
175
  </Text>
176
176
  </ModalHeader>
177
- {onboarding.step === 'saving-default-harness' ? (
178
- <div role="status" className="px-20 pb-8">
179
- <Text size="sm" className="text-foreground-neutral-muted">
180
- Saving harness default...
181
- </Text>
182
- </div>
183
- ) : null}
184
- {onboarding.step === 'saving-default-harness' && onboarding.error ? (
185
- <div className="px-20 pb-8">
186
- <Callout role="alert" type="error">
187
- <div className="flex flex-col gap-8">
188
- <Text size="sm" bold>
189
- Could not save default harness
177
+ {onboarding.step === 'configure-provider' ||
178
+ onboarding.step === 'saving-default-harness' ? (
179
+ <div className="flex min-h-0 flex-1 flex-col gap-inline">
180
+ {onboarding.step === 'saving-default-harness' ? (
181
+ <div role="status" className="px-row">
182
+ <Text size="sm" className="text-foreground-neutral-muted">
183
+ Saving harness default...
190
184
  </Text>
191
- <Text size="sm">{onboarding.error}</Text>
192
185
  </div>
193
- </Callout>
186
+ ) : null}
187
+ {onboarding.step === 'saving-default-harness' && onboarding.error ? (
188
+ <div className="px-row">
189
+ <Callout role="alert" type="error">
190
+ <div className="flex flex-col gap-inline">
191
+ <Text size="sm" bold>
192
+ Could not save default harness
193
+ </Text>
194
+ <Text size="sm">{onboarding.error}</Text>
195
+ </div>
196
+ </Callout>
197
+ </div>
198
+ ) : null}
199
+ <div className="flex min-h-0 flex-1 flex-col">
200
+ <ModelProviderTestAndSaveForm
201
+ workspaceId={workspaceId}
202
+ entry={onboarding.provider}
203
+ setAsDefaultOnSave
204
+ onSaved={() => {
205
+ void handleProviderSaved();
206
+ }}
207
+ />
208
+ </div>
194
209
  </div>
195
210
  ) : null}
196
- {onboarding.step === 'configure-provider' ||
197
- onboarding.step === 'saving-default-harness' ? (
198
- <ModelProviderTestAndSaveForm
199
- workspaceId={workspaceId}
200
- entry={onboarding.provider}
201
- setAsDefaultOnSave
202
- onSaved={() => {
203
- void handleProviderSaved();
204
- }}
205
- />
206
- ) : null}
207
211
  </ModalContent>
208
212
  </Modal>
209
213
  </div>
@@ -226,14 +230,14 @@ function HarnessCard({harness, onChoose}: {harness: HarnessDescriptor; onChoose:
226
230
  <button
227
231
  type="button"
228
232
  className={cn(
229
- 'group block w-full cursor-pointer p-16 text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
233
+ 'group block w-full cursor-pointer p-panel-compact text-left outline-none transition-colors hover:bg-background-components-hover focus-visible:shadow-button-neutral-focus',
230
234
  SURFACE_CLASS,
231
235
  )}
232
236
  aria-label={`Choose ${harness.label}`}
233
237
  onClick={onChoose}
234
238
  >
235
- <div className="flex min-w-0 items-center justify-between gap-12">
236
- <div className="flex min-w-0 flex-col gap-4">
239
+ <div className="flex min-w-0 items-center justify-between gap-cluster">
240
+ <div className="flex min-w-0 flex-col gap-tight">
237
241
  <Text size="md" bold className="min-w-0 truncate">
238
242
  {harness.label}
239
243
  </Text>
@@ -241,7 +245,7 @@ function HarnessCard({harness, onChoose}: {harness: HarnessDescriptor; onChoose:
241
245
  {harness.description}
242
246
  </Text>
243
247
  </div>
244
- <div className="flex shrink-0 items-center gap-4 text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
248
+ <div className="flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
245
249
  <Text size="sm">Choose</Text>
246
250
  <Icon name="chevronRight" className="size-16" />
247
251
  </div>
@@ -5,7 +5,7 @@ export default defineRoute({
5
5
  component: () => {
6
6
  const workspace = useActiveWorkspace();
7
7
  return (
8
- <div className="flex flex-col gap-32">
8
+ <div className="flex flex-col gap-region">
9
9
  <WorkspaceHarnessesSection workspaceId={workspace.id} />
10
10
  <WorkspaceModelProvidersSection workspaceId={workspace.id} />
11
11
  </div>