@shipfox/api-agent-dto 2.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 +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +48 -0
- package/LICENSE +21 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/catalog.d.ts +190 -0
- package/dist/schemas/catalog.d.ts.map +1 -0
- package/dist/schemas/catalog.js +231 -0
- package/dist/schemas/catalog.js.map +1 -0
- package/dist/schemas/custom-model-provider.d.ts +187 -0
- package/dist/schemas/custom-model-provider.d.ts.map +1 -0
- package/dist/schemas/custom-model-provider.js +168 -0
- package/dist/schemas/custom-model-provider.js.map +1 -0
- package/dist/schemas/harness.d.ts +49 -0
- package/dist/schemas/harness.d.ts.map +1 -0
- package/dist/schemas/harness.js +135 -0
- package/dist/schemas/harness.js.map +1 -0
- package/dist/schemas/index.d.ts +9 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +10 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/materialized-agent-step-config.d.ts +186 -0
- package/dist/schemas/materialized-agent-step-config.d.ts.map +1 -0
- package/dist/schemas/materialized-agent-step-config.js +56 -0
- package/dist/schemas/materialized-agent-step-config.js.map +1 -0
- package/dist/schemas/model-provider-config.d.ts +108 -0
- package/dist/schemas/model-provider-config.d.ts.map +1 -0
- package/dist/schemas/model-provider-config.js +54 -0
- package/dist/schemas/model-provider-config.js.map +1 -0
- package/dist/schemas/model-provider-id.d.ts +81 -0
- package/dist/schemas/model-provider-id.d.ts.map +1 -0
- package/dist/schemas/model-provider-id.js +53 -0
- package/dist/schemas/model-provider-id.js.map +1 -0
- package/dist/schemas/runtime-config.d.ts +51 -0
- package/dist/schemas/runtime-config.d.ts.map +1 -0
- package/dist/schemas/runtime-config.js +31 -0
- package/dist/schemas/runtime-config.js.map +1 -0
- package/dist/schemas/workspace-defaults.d.ts +16 -0
- package/dist/schemas/workspace-defaults.d.ts.map +1 -0
- package/dist/schemas/workspace-defaults.js +10 -0
- package/dist/schemas/workspace-defaults.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +56 -0
- package/src/index.ts +122 -0
- package/src/schemas/catalog.test.ts +206 -0
- package/src/schemas/catalog.ts +304 -0
- package/src/schemas/custom-model-provider.test.ts +294 -0
- package/src/schemas/custom-model-provider.ts +258 -0
- package/src/schemas/harness.test.ts +133 -0
- package/src/schemas/harness.ts +210 -0
- package/src/schemas/index.ts +136 -0
- package/src/schemas/materialized-agent-step-config.test.ts +198 -0
- package/src/schemas/materialized-agent-step-config.ts +69 -0
- package/src/schemas/model-provider-config.test.ts +152 -0
- package/src/schemas/model-provider-config.ts +88 -0
- package/src/schemas/model-provider-id.ts +60 -0
- package/src/schemas/runtime-config.test.ts +132 -0
- package/src/schemas/runtime-config.ts +38 -0
- package/src/schemas/workspace-defaults.ts +14 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +8 -0
- package/vitest.config.ts +3 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getModelProviderCredentialKeys,
|
|
3
|
+
listModelProviderConfigsResponseSchema,
|
|
4
|
+
modelProviderConfigDtoSchema,
|
|
5
|
+
modelProviderCredentialKeysMatch,
|
|
6
|
+
setDefaultModelProviderBodySchema,
|
|
7
|
+
setDefaultModelProviderResponseSchema,
|
|
8
|
+
updateModelProviderConfigBodySchema,
|
|
9
|
+
updateModelProviderDefaultModelBodySchema,
|
|
10
|
+
} from './model-provider-config.js';
|
|
11
|
+
|
|
12
|
+
describe('model provider config schemas', () => {
|
|
13
|
+
it('parses an update body with non-empty credentials', () => {
|
|
14
|
+
const parsed = updateModelProviderConfigBodySchema.parse({
|
|
15
|
+
default_model: 'claude-opus-4-8',
|
|
16
|
+
credentials: {api_key: 'rotated-secret'},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(parsed.default_model).toBe('claude-opus-4-8');
|
|
20
|
+
expect(parsed.credentials.api_key).toBe('rotated-secret');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('parses an update body with Latest selected', () => {
|
|
24
|
+
const parsed = updateModelProviderConfigBodySchema.parse({
|
|
25
|
+
default_model: null,
|
|
26
|
+
credentials: {api_key: 'rotated-secret'},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(parsed.default_model).toBeNull();
|
|
30
|
+
expect(parsed.credentials.api_key).toBe('rotated-secret');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('rejects an update body with empty credentials', () => {
|
|
34
|
+
const parse = () => updateModelProviderConfigBodySchema.parse({credentials: {}});
|
|
35
|
+
|
|
36
|
+
expect(parse).toThrow();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('rejects an update body with empty credential keys', () => {
|
|
40
|
+
const parse = () =>
|
|
41
|
+
updateModelProviderConfigBodySchema.parse({
|
|
42
|
+
credentials: {'': 'rotated-secret'},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(parse).toThrow();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('rejects an update body with empty credential values', () => {
|
|
49
|
+
const parse = () =>
|
|
50
|
+
updateModelProviderConfigBodySchema.parse({
|
|
51
|
+
credentials: {api_key: ''},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(parse).toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('exposes provider-aware credential key validation for update routes', () => {
|
|
58
|
+
const parsed = updateModelProviderConfigBodySchema.parse({
|
|
59
|
+
credentials: {token: 'rotated-secret'},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const keysMatch = modelProviderCredentialKeysMatch('openai', parsed.credentials);
|
|
63
|
+
|
|
64
|
+
expect(keysMatch).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('parses a model-only update body with Latest selected', () => {
|
|
68
|
+
const parsed = updateModelProviderDefaultModelBodySchema.parse({default_model: null});
|
|
69
|
+
|
|
70
|
+
expect(parsed.default_model).toBeNull();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('parses a model-only update body with an explicit model', () => {
|
|
74
|
+
const parsed = updateModelProviderDefaultModelBodySchema.parse({
|
|
75
|
+
default_model: 'claude-haiku-4-5',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
expect(parsed.default_model).toBe('claude-haiku-4-5');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('returns credential keys for route-layer validation', () => {
|
|
82
|
+
const keys = getModelProviderCredentialKeys('cloudflare-ai-gateway');
|
|
83
|
+
|
|
84
|
+
expect(keys).toEqual(['account_id', 'api_key', 'gateway_id']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('parses a set-default body for a supported provider', () => {
|
|
88
|
+
const parsed = setDefaultModelProviderBodySchema.parse({provider_id: 'anthropic'});
|
|
89
|
+
|
|
90
|
+
expect(parsed.provider_id).toBe('anthropic');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('parses a set-default body for an unsupported but valid provider ref', () => {
|
|
94
|
+
const parsed = setDefaultModelProviderBodySchema.parse({provider_id: 'openai-codex'});
|
|
95
|
+
|
|
96
|
+
expect(parsed.provider_id).toBe('openai-codex');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('parses a set-default response with a nullable provider', () => {
|
|
100
|
+
const parsed = setDefaultModelProviderResponseSchema.parse({default_provider_id: null});
|
|
101
|
+
|
|
102
|
+
expect(parsed.default_provider_id).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('parses config rows and list responses with a nullable default model provider', () => {
|
|
106
|
+
const row = {
|
|
107
|
+
kind: 'builtin',
|
|
108
|
+
provider_id: 'openai',
|
|
109
|
+
default_model: null,
|
|
110
|
+
created_at: '2026-06-27T10:30:00.000Z',
|
|
111
|
+
updated_at: '2026-06-27T10:45:00.000Z',
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const parsedRow = modelProviderConfigDtoSchema.parse(row);
|
|
115
|
+
const parsedList = listModelProviderConfigsResponseSchema.parse({
|
|
116
|
+
configs: [row],
|
|
117
|
+
default_harness_id: null,
|
|
118
|
+
default_provider_id: null,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(parsedList.default_harness_id).toBeNull();
|
|
122
|
+
expect(parsedRow.provider_id).toBe('openai');
|
|
123
|
+
expect(parsedRow.default_model).toBeNull();
|
|
124
|
+
expect(parsedList.default_provider_id).toBeNull();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('parses custom config rows in list responses', () => {
|
|
128
|
+
const parsedList = listModelProviderConfigsResponseSchema.parse({
|
|
129
|
+
configs: [
|
|
130
|
+
{
|
|
131
|
+
kind: 'custom',
|
|
132
|
+
provider_id: 'local-vllm',
|
|
133
|
+
display_name: 'Local vLLM',
|
|
134
|
+
api: 'openai-responses',
|
|
135
|
+
base_url: 'https://llm.example.test/v1',
|
|
136
|
+
headers: [],
|
|
137
|
+
secret_header_names: ['authorization'],
|
|
138
|
+
models: [{id: 'llama-3.1', label: 'Llama 3.1'}],
|
|
139
|
+
default_model: null,
|
|
140
|
+
created_at: '2026-06-27T10:30:00.000Z',
|
|
141
|
+
updated_at: '2026-06-27T10:45:00.000Z',
|
|
142
|
+
},
|
|
143
|
+
],
|
|
144
|
+
default_harness_id: 'claude',
|
|
145
|
+
default_provider_id: 'local-vllm',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(parsedList.configs[0]?.kind).toBe('custom');
|
|
149
|
+
expect(parsedList.default_harness_id).toBe('claude');
|
|
150
|
+
expect(parsedList.default_provider_id).toBe('local-vllm');
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
import {getModelProviderEntry, harnessSchema} from './catalog.js';
|
|
3
|
+
import {customModelProviderConfigDtoSchema} from './custom-model-provider.js';
|
|
4
|
+
import {modelProviderRefSchema, type SupportedModelProviderId} from './model-provider-id.js';
|
|
5
|
+
|
|
6
|
+
const credentialKeySchema = z.string().min(1);
|
|
7
|
+
const credentialRecordSchema = z.record(credentialKeySchema, z.string().min(1));
|
|
8
|
+
|
|
9
|
+
export const modelProviderConfigDtoSchema = z.object({
|
|
10
|
+
kind: z.literal('builtin'),
|
|
11
|
+
provider_id: modelProviderRefSchema,
|
|
12
|
+
default_model: z.string().min(1).nullable(),
|
|
13
|
+
created_at: z.string().datetime(),
|
|
14
|
+
updated_at: z.string().datetime(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
export type ModelProviderConfigDto = z.infer<typeof modelProviderConfigDtoSchema>;
|
|
18
|
+
|
|
19
|
+
export const modelProviderConfigResponseSchema = z.discriminatedUnion('kind', [
|
|
20
|
+
modelProviderConfigDtoSchema,
|
|
21
|
+
customModelProviderConfigDtoSchema,
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
export type ModelProviderConfigResponseDto = z.infer<typeof modelProviderConfigResponseSchema>;
|
|
25
|
+
|
|
26
|
+
export const listModelProviderConfigsResponseSchema = z.object({
|
|
27
|
+
configs: z.array(modelProviderConfigResponseSchema),
|
|
28
|
+
default_provider_id: modelProviderRefSchema.nullable(),
|
|
29
|
+
default_harness_id: harnessSchema.nullable(),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
export type ListModelProviderConfigsResponseDto = z.infer<
|
|
33
|
+
typeof listModelProviderConfigsResponseSchema
|
|
34
|
+
>;
|
|
35
|
+
|
|
36
|
+
export const updateModelProviderConfigBodySchema = z.object({
|
|
37
|
+
default_model: z.string().min(1).nullable().optional(),
|
|
38
|
+
credentials: credentialRecordSchema.refine((credentials) => Object.keys(credentials).length > 0, {
|
|
39
|
+
message: 'Credentials must include at least one key.',
|
|
40
|
+
}),
|
|
41
|
+
set_as_default: z.boolean().optional(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
export type UpdateModelProviderConfigBodyDto = z.infer<typeof updateModelProviderConfigBodySchema>;
|
|
45
|
+
|
|
46
|
+
export const updateModelProviderDefaultModelBodySchema = z.object({
|
|
47
|
+
default_model: z.string().min(1).nullable(),
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
export type UpdateModelProviderDefaultModelBodyDto = z.infer<
|
|
51
|
+
typeof updateModelProviderDefaultModelBodySchema
|
|
52
|
+
>;
|
|
53
|
+
|
|
54
|
+
export const setDefaultModelProviderBodySchema = z.object({
|
|
55
|
+
provider_id: modelProviderRefSchema,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
export type SetDefaultModelProviderBodyDto = z.infer<typeof setDefaultModelProviderBodySchema>;
|
|
59
|
+
|
|
60
|
+
export const setDefaultModelProviderResponseSchema = z.object({
|
|
61
|
+
default_provider_id: modelProviderRefSchema.nullable(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
export type SetDefaultModelProviderResponseDto = z.infer<
|
|
65
|
+
typeof setDefaultModelProviderResponseSchema
|
|
66
|
+
>;
|
|
67
|
+
|
|
68
|
+
export function getModelProviderCredentialKeys(
|
|
69
|
+
providerId: SupportedModelProviderId,
|
|
70
|
+
): string[] | undefined {
|
|
71
|
+
const entry = getModelProviderEntry(providerId);
|
|
72
|
+
if (entry === undefined || entry.support_status !== 'supported') return undefined;
|
|
73
|
+
return entry.credential_fields.map((field) => field.key).sort();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function modelProviderCredentialKeysMatch(
|
|
77
|
+
providerId: SupportedModelProviderId,
|
|
78
|
+
credentials: Record<string, string>,
|
|
79
|
+
): boolean {
|
|
80
|
+
const expectedKeys = getModelProviderCredentialKeys(providerId);
|
|
81
|
+
if (expectedKeys === undefined) return false;
|
|
82
|
+
return sameKeys(Object.keys(credentials).sort(), expectedKeys);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function sameKeys(actualKeys: string[], expectedKeys: string[]): boolean {
|
|
86
|
+
if (actualKeys.length !== expectedKeys.length) return false;
|
|
87
|
+
return actualKeys.every((key, index) => key === expectedKeys[index]);
|
|
88
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
|
|
3
|
+
export const SUPPORTED_MODEL_PROVIDER_IDS = [
|
|
4
|
+
'anthropic',
|
|
5
|
+
'ant-ling',
|
|
6
|
+
'azure-openai-responses',
|
|
7
|
+
'openai',
|
|
8
|
+
'deepseek',
|
|
9
|
+
'nvidia',
|
|
10
|
+
'google',
|
|
11
|
+
'mistral',
|
|
12
|
+
'groq',
|
|
13
|
+
'cerebras',
|
|
14
|
+
'cloudflare-ai-gateway',
|
|
15
|
+
'cloudflare-workers-ai',
|
|
16
|
+
'xai',
|
|
17
|
+
'openrouter',
|
|
18
|
+
'vercel-ai-gateway',
|
|
19
|
+
'zai',
|
|
20
|
+
'zai-coding-cn',
|
|
21
|
+
'opencode',
|
|
22
|
+
'opencode-go',
|
|
23
|
+
'huggingface',
|
|
24
|
+
'fireworks',
|
|
25
|
+
'together',
|
|
26
|
+
'kimi-coding',
|
|
27
|
+
'minimax',
|
|
28
|
+
'minimax-cn',
|
|
29
|
+
'moonshotai',
|
|
30
|
+
'moonshotai-cn',
|
|
31
|
+
'xiaomi',
|
|
32
|
+
'xiaomi-token-plan-cn',
|
|
33
|
+
'xiaomi-token-plan-ams',
|
|
34
|
+
'xiaomi-token-plan-sgp',
|
|
35
|
+
] as const;
|
|
36
|
+
|
|
37
|
+
export const UNSUPPORTED_MODEL_PROVIDER_IDS = [
|
|
38
|
+
'amazon-bedrock',
|
|
39
|
+
'google-vertex',
|
|
40
|
+
'openai-codex',
|
|
41
|
+
'github-copilot',
|
|
42
|
+
] as const;
|
|
43
|
+
|
|
44
|
+
export const MODEL_PROVIDER_IDS = [
|
|
45
|
+
...SUPPORTED_MODEL_PROVIDER_IDS,
|
|
46
|
+
...UNSUPPORTED_MODEL_PROVIDER_IDS,
|
|
47
|
+
] as const;
|
|
48
|
+
|
|
49
|
+
export const supportedModelProviderIdSchema = z.enum(SUPPORTED_MODEL_PROVIDER_IDS);
|
|
50
|
+
export const providerIdSchema = z.enum(MODEL_PROVIDER_IDS);
|
|
51
|
+
export const MODEL_PROVIDER_SLUG_PATTERN = /^[a-z0-9][a-z0-9-]{1,38}[a-z0-9]$/;
|
|
52
|
+
export const modelProviderRefSchema = z.string().regex(MODEL_PROVIDER_SLUG_PATTERN);
|
|
53
|
+
|
|
54
|
+
export type SupportedModelProviderId = z.infer<typeof supportedModelProviderIdSchema>;
|
|
55
|
+
export type ModelProviderId = z.infer<typeof providerIdSchema>;
|
|
56
|
+
export type ModelProviderRef = z.infer<typeof modelProviderRefSchema>;
|
|
57
|
+
|
|
58
|
+
export function isReservedModelProviderId(value: string): boolean {
|
|
59
|
+
return (MODEL_PROVIDER_IDS as readonly string[]).includes(value);
|
|
60
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {agentRuntimeCredentialsResponseSchema} from './runtime-config.js';
|
|
2
|
+
|
|
3
|
+
describe('agentRuntimeCredentialsResponseSchema', () => {
|
|
4
|
+
it('parses runtime credentials for a supported provider', () => {
|
|
5
|
+
const parsed = agentRuntimeCredentialsResponseSchema.parse({
|
|
6
|
+
harness: 'pi',
|
|
7
|
+
provider_id: 'anthropic',
|
|
8
|
+
model: 'claude-opus-4-8',
|
|
9
|
+
thinking: 'high',
|
|
10
|
+
credentials: {api_key: 'secret'},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(parsed.provider_id).toBe('anthropic');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('parses runtime credentials with a custom model provider descriptor', () => {
|
|
17
|
+
const parsed = agentRuntimeCredentialsResponseSchema.parse({
|
|
18
|
+
harness: 'pi',
|
|
19
|
+
provider_id: 'local-vllm',
|
|
20
|
+
model: 'llama-3.1',
|
|
21
|
+
thinking: 'high',
|
|
22
|
+
credentials: {api_key: 'secret', authorization: 'Bearer secret'},
|
|
23
|
+
custom_provider: {
|
|
24
|
+
api: 'openai-responses',
|
|
25
|
+
base_url: 'https://llm.example.test/v1',
|
|
26
|
+
headers: [{name: 'x-region', value: 'local'}],
|
|
27
|
+
secret_header_names: ['authorization'],
|
|
28
|
+
models: [{id: 'llama-3.1', label: 'Llama 3.1'}],
|
|
29
|
+
requires_api_key: true,
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(parsed.provider_id).toBe('local-vllm');
|
|
34
|
+
expect(parsed.custom_provider?.api).toBe('openai-responses');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('rejects a custom model provider descriptor without key intent', () => {
|
|
38
|
+
const parse = () =>
|
|
39
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
40
|
+
harness: 'pi',
|
|
41
|
+
provider_id: 'local-vllm',
|
|
42
|
+
model: 'llama-3.1',
|
|
43
|
+
thinking: 'high',
|
|
44
|
+
credentials: {api_key: 'secret'},
|
|
45
|
+
custom_provider: {
|
|
46
|
+
api: 'openai-responses',
|
|
47
|
+
base_url: 'https://llm.example.test/v1',
|
|
48
|
+
headers: [],
|
|
49
|
+
secret_header_names: [],
|
|
50
|
+
models: [{id: 'llama-3.1', label: 'Llama 3.1'}],
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(parse).toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('rejects custom model provider runtime credentials without a custom model provider descriptor', () => {
|
|
58
|
+
const parse = () =>
|
|
59
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
60
|
+
harness: 'pi',
|
|
61
|
+
provider_id: 'local-vllm',
|
|
62
|
+
model: 'llama-3.1',
|
|
63
|
+
thinking: 'high',
|
|
64
|
+
credentials: {api_key: 'secret'},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(parse).toThrow();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('rejects a response without a model', () => {
|
|
71
|
+
const parse = () =>
|
|
72
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
73
|
+
harness: 'pi',
|
|
74
|
+
provider_id: 'anthropic',
|
|
75
|
+
thinking: 'high',
|
|
76
|
+
credentials: {api_key: 'secret'},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
expect(parse).toThrow();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('rejects a response without thinking', () => {
|
|
83
|
+
const parse = () =>
|
|
84
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
85
|
+
harness: 'pi',
|
|
86
|
+
provider_id: 'anthropic',
|
|
87
|
+
model: 'claude-opus-4-8',
|
|
88
|
+
credentials: {api_key: 'secret'},
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
expect(parse).toThrow();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('rejects runtime credentials for an invalid provider ref', () => {
|
|
95
|
+
const parse = () =>
|
|
96
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
97
|
+
harness: 'pi',
|
|
98
|
+
provider_id: 'bad_provider',
|
|
99
|
+
model: 'gpt-5.5-pro',
|
|
100
|
+
thinking: 'high',
|
|
101
|
+
credentials: {api_key: 'secret'},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(parse).toThrow();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('rejects runtime credentials with an empty key', () => {
|
|
108
|
+
const parse = () =>
|
|
109
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
110
|
+
harness: 'pi',
|
|
111
|
+
provider_id: 'anthropic',
|
|
112
|
+
model: 'claude-opus-4-8',
|
|
113
|
+
thinking: 'high',
|
|
114
|
+
credentials: {'': 'secret'},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(parse).toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('rejects runtime credentials with an empty value', () => {
|
|
121
|
+
const parse = () =>
|
|
122
|
+
agentRuntimeCredentialsResponseSchema.parse({
|
|
123
|
+
harness: 'pi',
|
|
124
|
+
provider_id: 'anthropic',
|
|
125
|
+
model: 'claude-opus-4-8',
|
|
126
|
+
thinking: 'high',
|
|
127
|
+
credentials: {api_key: ''},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(parse).toThrow();
|
|
131
|
+
});
|
|
132
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {agentThinkingSchema, harnessSchema} from '@shipfox/workflow-document';
|
|
2
|
+
import {z} from 'zod';
|
|
3
|
+
import {customModelProviderRuntimeConfigSchema} from './custom-model-provider.js';
|
|
4
|
+
import {isReservedModelProviderId, modelProviderRefSchema} from './model-provider-id.js';
|
|
5
|
+
|
|
6
|
+
const credentialKeySchema = z.string().min(1);
|
|
7
|
+
const credentialValueSchema = z.string().min(1);
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Lease-scoped runtime credentials. The credential values are secrets and must
|
|
11
|
+
* never be written to logs, traces, client state, or generic catalog surfaces.
|
|
12
|
+
*/
|
|
13
|
+
export const agentRuntimeCredentialsResponseSchema = z
|
|
14
|
+
.object({
|
|
15
|
+
harness: harnessSchema,
|
|
16
|
+
provider_id: modelProviderRefSchema,
|
|
17
|
+
model: z.string().min(1),
|
|
18
|
+
thinking: agentThinkingSchema,
|
|
19
|
+
credentials: z.record(credentialKeySchema, credentialValueSchema),
|
|
20
|
+
custom_provider: customModelProviderRuntimeConfigSchema.optional(),
|
|
21
|
+
})
|
|
22
|
+
.superRefine((response, ctx) => {
|
|
23
|
+
if (isReservedModelProviderId(response.provider_id) || response.custom_provider !== undefined) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
ctx.addIssue({
|
|
28
|
+
code: 'custom',
|
|
29
|
+
path: ['custom_provider'],
|
|
30
|
+
message: 'Custom model provider runtime config is required for custom model provider refs.',
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
export type AgentRuntimeCredentialsResponseDto = z.infer<
|
|
35
|
+
typeof agentRuntimeCredentialsResponseSchema
|
|
36
|
+
>;
|
|
37
|
+
export type {CustomModelProviderRuntimeConfigDto} from './custom-model-provider.js';
|
|
38
|
+
export {customModelProviderRuntimeConfigSchema};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {z} from 'zod';
|
|
2
|
+
import {harnessSchema} from './catalog.js';
|
|
3
|
+
|
|
4
|
+
export const setDefaultHarnessBodySchema = z.object({
|
|
5
|
+
harness_id: harnessSchema,
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export type SetDefaultHarnessBodyDto = z.infer<typeof setDefaultHarnessBodySchema>;
|
|
9
|
+
|
|
10
|
+
export const setDefaultHarnessResponseSchema = z.object({
|
|
11
|
+
default_harness_id: harnessSchema,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export type SetDefaultHarnessResponseDto = z.infer<typeof setDefaultHarnessResponseSchema>;
|