@shipfox/client-agent 23.0.0 → 25.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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/components/agent-workflow-example.d.ts.map +1 -1
- package/dist/components/agent-workflow-example.js +0 -1
- package/dist/components/agent-workflow-example.js.map +1 -1
- package/dist/components/model-providers-section.stories.js +2 -1
- package/dist/components/model-providers-section.stories.js.map +1 -1
- package/dist/core/models.d.ts +2 -0
- package/dist/core/models.d.ts.map +1 -1
- package/dist/core/models.js.map +1 -1
- package/dist/core/provider-policy.d.ts.map +1 -1
- package/dist/core/provider-policy.js +8 -2
- package/dist/core/provider-policy.js.map +1 -1
- package/dist/hooks/api/model-provider-mapper.d.ts.map +1 -1
- package/dist/hooks/api/model-provider-mapper.js +3 -1
- package/dist/hooks/api/model-provider-mapper.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/agent-workflow-example.test.ts +2 -2
- package/src/components/agent-workflow-example.ts +0 -1
- package/src/components/harness-availability.test.ts +4 -1
- package/src/components/model-providers-section.stories.tsx +3 -1
- package/src/components/model-providers-section.test.tsx +28 -1
- package/src/core/models.ts +2 -0
- package/src/core/provider-policy.test.ts +78 -0
- package/src/core/provider-policy.ts +8 -2
- package/src/hooks/api/model-provider-mapper.test.ts +19 -1
- package/src/hooks/api/model-provider-mapper.ts +2 -0
- package/src/pages/model-provider-onboarding-page.test.tsx +7 -5
- package/test/fixtures/model-providers.ts +3 -0
- 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": "
|
|
4
|
+
"version": "25.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -28,10 +28,10 @@
|
|
|
28
28
|
"@swc/helpers": "^0.5.17",
|
|
29
29
|
"@tanstack/react-form": "^1.32.0",
|
|
30
30
|
"@tanstack/react-query": "^5.101.0",
|
|
31
|
-
"@shipfox/api-agent-dto": "14.0.0",
|
|
32
31
|
"@shipfox/client-api": "6.0.1",
|
|
33
|
-
"@shipfox/client-shell": "
|
|
34
|
-
"@shipfox/client-ui": "
|
|
32
|
+
"@shipfox/client-shell": "25.0.0",
|
|
33
|
+
"@shipfox/client-ui": "24.0.0",
|
|
34
|
+
"@shipfox/api-agent-dto": "16.0.0",
|
|
35
35
|
"@shipfox/react-ui": "2.2.0"
|
|
36
36
|
},
|
|
37
37
|
"peerDependencies": {
|
|
@@ -68,7 +68,7 @@ describe('buildAgentWorkflowExample', () => {
|
|
|
68
68
|
expect(lines).toContain(expectedHarness);
|
|
69
69
|
expect(lines).toContain(expectedProvider);
|
|
70
70
|
expect(lines).toContain(expectedModel);
|
|
71
|
-
expect(example.highlightedLineRange).toEqual({startLine:
|
|
72
|
-
expect(lines.slice(
|
|
71
|
+
expect(example.highlightedLineRange).toEqual({startLine: 10, endLine: 12});
|
|
72
|
+
expect(lines.slice(9, 12)).toEqual([expectedHarness, expectedProvider, expectedModel]);
|
|
73
73
|
});
|
|
74
74
|
});
|
|
@@ -44,7 +44,9 @@ describe('harness availability', () => {
|
|
|
44
44
|
|
|
45
45
|
test('marks harnesses available from a managed-only catalog without workspace configs', () => {
|
|
46
46
|
const catalog = toProviderCatalog(
|
|
47
|
-
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled'
|
|
47
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled', {
|
|
48
|
+
managedProviderId: 'shipfox',
|
|
49
|
+
}),
|
|
48
50
|
);
|
|
49
51
|
|
|
50
52
|
expect(isHarnessAvailable(getHarness('pi'), [], catalog)).toBe(true);
|
|
@@ -61,6 +63,7 @@ describe('harness availability', () => {
|
|
|
61
63
|
}),
|
|
62
64
|
],
|
|
63
65
|
'disabled',
|
|
66
|
+
{managedProviderId: 'shipfox'},
|
|
64
67
|
),
|
|
65
68
|
);
|
|
66
69
|
|
|
@@ -239,7 +239,9 @@ function fetchForScenario(scenario: Scenario): typeof fetch {
|
|
|
239
239
|
return Promise.resolve(
|
|
240
240
|
jsonResponse({
|
|
241
241
|
providers: catalogForScenario(scenario),
|
|
242
|
-
...(scenario === 'managed-only'
|
|
242
|
+
...(scenario === 'managed-only'
|
|
243
|
+
? {workspace_providers: 'disabled', managed_provider_id: 'shipfox'}
|
|
244
|
+
: {}),
|
|
243
245
|
}),
|
|
244
246
|
);
|
|
245
247
|
}
|
|
@@ -66,7 +66,11 @@ describe('WorkspaceModelProvidersSection', () => {
|
|
|
66
66
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
67
67
|
if (requestPath(input).endsWith('/agent/model-provider-catalog')) {
|
|
68
68
|
return Promise.resolve(
|
|
69
|
-
jsonResponse(
|
|
69
|
+
jsonResponse(
|
|
70
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled', {
|
|
71
|
+
managedProviderId: 'shipfox',
|
|
72
|
+
}),
|
|
73
|
+
),
|
|
70
74
|
);
|
|
71
75
|
}
|
|
72
76
|
return Promise.resolve(
|
|
@@ -101,6 +105,29 @@ describe('WorkspaceModelProvidersSection', () => {
|
|
|
101
105
|
expect(usageDialog).toHaveTextContent('model: claude-opus-4-8');
|
|
102
106
|
});
|
|
103
107
|
|
|
108
|
+
test('renders managed provider models from an older managed-only catalog response', async () => {
|
|
109
|
+
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
110
|
+
if (requestPath(input).endsWith('/agent/model-provider-catalog')) {
|
|
111
|
+
return Promise.resolve(
|
|
112
|
+
jsonResponse({
|
|
113
|
+
providers: [managedModelProviderEntry()],
|
|
114
|
+
workspace_providers: 'disabled',
|
|
115
|
+
}),
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return Promise.resolve(
|
|
119
|
+
jsonResponse(modelProviderConfigsResponse({configs: [], default_provider_id: null})),
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
configureApiClient({baseUrl: 'https://api.example.test', fetchImpl});
|
|
123
|
+
|
|
124
|
+
renderModelProviders(<WorkspaceModelProvidersSection workspaceId={AGENT_TEST_WORKSPACE_ID} />);
|
|
125
|
+
|
|
126
|
+
expect(await screen.findByText('Managed provider')).toBeVisible();
|
|
127
|
+
expect(screen.getByText('Shipfox Managed')).toBeVisible();
|
|
128
|
+
expect(screen.queryByText('Managed provider unavailable')).not.toBeInTheDocument();
|
|
129
|
+
});
|
|
130
|
+
|
|
104
131
|
test('renders configured, available, and unsupported providers', async () => {
|
|
105
132
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
106
133
|
if (requestPath(input).endsWith('/agent/model-provider-catalog')) {
|
package/src/core/models.ts
CHANGED
|
@@ -79,6 +79,8 @@ export type ProviderConfig = BuiltinProviderConfig | CustomProviderConfig;
|
|
|
79
79
|
export interface ProviderCatalog {
|
|
80
80
|
readonly providers: readonly ProviderCatalogEntry[];
|
|
81
81
|
readonly workspaceProviders: WorkspaceProvidersPolicy;
|
|
82
|
+
readonly managedProviderId: string | null;
|
|
83
|
+
readonly instanceDefaultProviderId: string | null;
|
|
82
84
|
}
|
|
83
85
|
|
|
84
86
|
export interface ProviderConfiguration {
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {toProviderCatalog} from '#hooks/api/model-provider-mapper.js';
|
|
2
|
+
import {
|
|
3
|
+
managedModelProviderEntry,
|
|
4
|
+
modelProviderCatalogResponse,
|
|
5
|
+
modelProviderEntry,
|
|
6
|
+
unsupportedModelProviderEntry,
|
|
7
|
+
} from '#test/fixtures/model-providers.js';
|
|
8
|
+
import {isManagedOnlyCatalog, managedProviderFromCatalog} from './provider-policy.js';
|
|
9
|
+
|
|
10
|
+
describe('managedProviderFromCatalog', () => {
|
|
11
|
+
test('returns the managed entry from a managed-only catalog', () => {
|
|
12
|
+
const catalog = toProviderCatalog(
|
|
13
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled', {
|
|
14
|
+
managedProviderId: 'shipfox',
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
expect(managedProviderFromCatalog(catalog)).toMatchObject({
|
|
19
|
+
id: 'shipfox',
|
|
20
|
+
kind: 'supported',
|
|
21
|
+
});
|
|
22
|
+
expect(isManagedOnlyCatalog(catalog)).toBe(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('returns the managed entry from a mixed catalog under the enabled policy', () => {
|
|
26
|
+
const catalog = toProviderCatalog(
|
|
27
|
+
modelProviderCatalogResponse([modelProviderEntry(), managedModelProviderEntry()], 'enabled', {
|
|
28
|
+
managedProviderId: 'shipfox',
|
|
29
|
+
}),
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
expect(managedProviderFromCatalog(catalog)).toMatchObject({
|
|
33
|
+
id: 'shipfox',
|
|
34
|
+
kind: 'supported',
|
|
35
|
+
});
|
|
36
|
+
expect(isManagedOnlyCatalog(catalog)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('falls back to the supported entry for older managed-only catalogs', () => {
|
|
40
|
+
const catalog = toProviderCatalog(
|
|
41
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled'),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
expect(managedProviderFromCatalog(catalog)).toMatchObject({
|
|
45
|
+
id: 'shipfox',
|
|
46
|
+
kind: 'supported',
|
|
47
|
+
});
|
|
48
|
+
expect(isManagedOnlyCatalog(catalog)).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('returns undefined without a managed provider id in a mixed catalog', () => {
|
|
52
|
+
const catalog = toProviderCatalog(modelProviderCatalogResponse());
|
|
53
|
+
|
|
54
|
+
expect(managedProviderFromCatalog(catalog)).toBeUndefined();
|
|
55
|
+
expect(managedProviderFromCatalog(undefined)).toBeUndefined();
|
|
56
|
+
expect(isManagedOnlyCatalog(catalog)).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('returns undefined when the managed provider id is absent from the catalog', () => {
|
|
60
|
+
const catalog = toProviderCatalog(
|
|
61
|
+
modelProviderCatalogResponse([modelProviderEntry()], 'enabled', {
|
|
62
|
+
managedProviderId: 'missing-provider',
|
|
63
|
+
}),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
expect(managedProviderFromCatalog(catalog)).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('returns undefined when the managed provider entry is unsupported', () => {
|
|
70
|
+
const catalog = toProviderCatalog(
|
|
71
|
+
modelProviderCatalogResponse([unsupportedModelProviderEntry()], 'enabled', {
|
|
72
|
+
managedProviderId: 'amazon-bedrock',
|
|
73
|
+
}),
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
expect(managedProviderFromCatalog(catalog)).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -21,8 +21,14 @@ export function isManagedOnlyCatalog(catalog: ProviderCatalog | undefined): bool
|
|
|
21
21
|
export function managedProviderFromCatalog(
|
|
22
22
|
catalog: ProviderCatalog | undefined,
|
|
23
23
|
): SupportedProvider | undefined {
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
const providers = catalog?.providers;
|
|
25
|
+
const managedProviderId = catalog?.managedProviderId;
|
|
26
|
+
if (providers === undefined) return undefined;
|
|
27
|
+
if (managedProviderId === null || managedProviderId === undefined) {
|
|
28
|
+
return isManagedOnlyCatalog(catalog) ? providers.find(isSupportedProvider) : undefined;
|
|
29
|
+
}
|
|
30
|
+
const managedEntry = providers.find((provider) => provider.id === managedProviderId);
|
|
31
|
+
return managedEntry !== undefined && isSupportedProvider(managedEntry) ? managedEntry : undefined;
|
|
26
32
|
}
|
|
27
33
|
|
|
28
34
|
export function availableProviders(
|
|
@@ -20,10 +20,14 @@ test('maps provider catalog entries before they reach the client domain', () =>
|
|
|
20
20
|
|
|
21
21
|
test('maps the managed-only workspace policy and managed provider models', () => {
|
|
22
22
|
const catalog = toProviderCatalog(
|
|
23
|
-
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled'
|
|
23
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled', {
|
|
24
|
+
managedProviderId: 'shipfox',
|
|
25
|
+
}),
|
|
24
26
|
);
|
|
25
27
|
|
|
26
28
|
expect(catalog.workspaceProviders).toBe('disabled');
|
|
29
|
+
expect(catalog.managedProviderId).toBe('shipfox');
|
|
30
|
+
expect(catalog.instanceDefaultProviderId).toBeNull();
|
|
27
31
|
expect(catalog.providers[0]).toEqual(
|
|
28
32
|
expect.objectContaining({
|
|
29
33
|
id: 'shipfox',
|
|
@@ -35,6 +39,20 @@ test('maps the managed-only workspace policy and managed provider models', () =>
|
|
|
35
39
|
);
|
|
36
40
|
});
|
|
37
41
|
|
|
42
|
+
test('maps the managed provider id and instance default provider id from a mixed catalog', () => {
|
|
43
|
+
const catalog = toProviderCatalog(
|
|
44
|
+
modelProviderCatalogResponse([modelProviderEntry(), managedModelProviderEntry()], 'enabled', {
|
|
45
|
+
managedProviderId: 'shipfox',
|
|
46
|
+
instanceDefaultProviderId: 'anthropic',
|
|
47
|
+
}),
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
expect(catalog.workspaceProviders).toBe('enabled');
|
|
51
|
+
expect(catalog.managedProviderId).toBe('shipfox');
|
|
52
|
+
expect(catalog.instanceDefaultProviderId).toBe('anthropic');
|
|
53
|
+
expect(catalog.providers).toHaveLength(2);
|
|
54
|
+
});
|
|
55
|
+
|
|
38
56
|
test('maps configuration response defaults and provider config fields', () => {
|
|
39
57
|
const configuration = toProviderConfiguration(modelProviderConfigsResponse());
|
|
40
58
|
|
|
@@ -24,6 +24,8 @@ export function toProviderCatalog(response: ModelProviderCatalogResponseDto): Pr
|
|
|
24
24
|
return {
|
|
25
25
|
providers: response.providers.map(toProviderCatalogEntry),
|
|
26
26
|
workspaceProviders: response.workspace_providers ?? 'enabled',
|
|
27
|
+
managedProviderId: response.managed_provider_id ?? null,
|
|
28
|
+
instanceDefaultProviderId: response.instance_default_provider_id ?? null,
|
|
27
29
|
};
|
|
28
30
|
}
|
|
29
31
|
|
|
@@ -79,11 +79,13 @@ describe('ModelProviderOnboardingPage', () => {
|
|
|
79
79
|
|
|
80
80
|
test('continues directly when workspace providers are managed by the instance', async () => {
|
|
81
81
|
const onConfigured = vi.fn();
|
|
82
|
-
const fetchImpl = vi
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
const fetchImpl = vi.fn().mockResolvedValue(
|
|
83
|
+
jsonResponse(
|
|
84
|
+
modelProviderCatalogResponse([managedModelProviderEntry()], 'disabled', {
|
|
85
|
+
managedProviderId: 'shipfox',
|
|
86
|
+
}),
|
|
87
|
+
),
|
|
88
|
+
);
|
|
87
89
|
configureApiClient({baseUrl: 'https://api.example.test', fetchImpl});
|
|
88
90
|
|
|
89
91
|
renderOnboarding(
|
|
@@ -113,10 +113,13 @@ export function customModelProviderConfig(
|
|
|
113
113
|
export function modelProviderCatalogResponse(
|
|
114
114
|
modelProviders: ModelProviderCatalogEntryDto[] = [modelProviderEntry()],
|
|
115
115
|
workspaceProviders?: WorkspaceProvidersPolicy,
|
|
116
|
+
providerIds: {managedProviderId?: string | null; instanceDefaultProviderId?: string | null} = {},
|
|
116
117
|
): ModelProviderCatalogResponseDto {
|
|
117
118
|
return {
|
|
118
119
|
providers: modelProviders,
|
|
119
120
|
...(workspaceProviders === undefined ? {} : {workspace_providers: workspaceProviders}),
|
|
121
|
+
managed_provider_id: providerIds.managedProviderId ?? null,
|
|
122
|
+
instance_default_provider_id: providerIds.instanceDefaultProviderId ?? null,
|
|
120
123
|
};
|
|
121
124
|
}
|
|
122
125
|
|