-
Notifications
You must be signed in to change notification settings - Fork 3.1k
refactor: extract provider metadata seam for Phase 2 registry readiness #1185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import type { ProviderCapabilities } from '../types'; | ||
|
|
||
| export const CLAUDE_CAPABILITIES: ProviderCapabilities = { | ||
| sessionResume: true, | ||
| mcp: true, | ||
| hooks: true, | ||
| skills: true, | ||
| toolRestrictions: true, | ||
| structuredOutput: true, | ||
| envInjection: true, | ||
| costControl: true, | ||
| effortControl: true, | ||
| thinkingControl: true, | ||
| fallbackModel: true, | ||
| sandbox: true, | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| import type { ProviderCapabilities } from '../types'; | ||
|
|
||
| export const CODEX_CAPABILITIES: ProviderCapabilities = { | ||
| sessionResume: true, | ||
| mcp: false, | ||
| hooks: false, | ||
| skills: false, | ||
| toolRestrictions: false, | ||
| structuredOutput: true, | ||
| envInjection: true, | ||
| costControl: false, | ||
| effortControl: false, | ||
| thinkingControl: false, | ||
| fallbackModel: false, | ||
| sandbox: false, | ||
| }; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||
| import { describe, it, expect } from 'bun:test'; | ||||||||||||||||||||||||||||||||||||||
| import { isClaudeModel, isModelCompatible } from './model-validation'; | ||||||||||||||||||||||||||||||||||||||
| import { isClaudeModel, isModelCompatible, inferProviderFromModel } from './model-validation'; | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe('model-validation', () => { | ||||||||||||||||||||||||||||||||||||||
| describe('isClaudeModel', () => { | ||||||||||||||||||||||||||||||||||||||
|
|
@@ -66,4 +66,30 @@ describe('model-validation', () => { | |||||||||||||||||||||||||||||||||||||
| expect(isModelCompatible('codex', '')).toBe(true); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| describe('inferProviderFromModel', () => { | ||||||||||||||||||||||||||||||||||||||
| it('should return default when model is undefined', () => { | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel(undefined, 'claude')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel(undefined, 'codex')).toBe('codex'); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it('should return default when model is empty string', () => { | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('', 'claude')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('', 'codex')).toBe('codex'); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it('should infer claude from Claude model names', () => { | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('sonnet', 'codex')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('opus', 'codex')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('haiku', 'codex')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('inherit', 'codex')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('claude-opus-4-6', 'codex')).toBe('claude'); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+81
to
+87
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't classify
Suggested test update it('should infer claude from Claude model names', () => {
expect(inferProviderFromModel('sonnet', 'codex')).toBe('claude');
expect(inferProviderFromModel('opus', 'codex')).toBe('claude');
expect(inferProviderFromModel('haiku', 'codex')).toBe('claude');
- expect(inferProviderFromModel('inherit', 'codex')).toBe('claude');
expect(inferProviderFromModel('claude-opus-4-6', 'codex')).toBe('claude');
});
+
+ it('should keep the default provider for inherit', () => {
+ expect(inferProviderFromModel('inherit', 'claude')).toBe('claude');
+ expect(inferProviderFromModel('inherit', 'codex')).toBe('codex');
+ });📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| it('should infer codex from non-Claude model names', () => { | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('gpt-5.3-codex', 'claude')).toBe('codex'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('gpt-4', 'claude')).toBe('codex'); | ||||||||||||||||||||||||||||||||||||||
| expect(inferProviderFromModel('o1-mini', 'claude')).toBe('codex'); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Return an immutable snapshot here.
Line 52 and Line 54 hand out the shared capability singletons by reference. Because those same objects are now part of the public surface, one accidental mutation by a consumer can silently change later capability checks for every caller in-process.
Possible fix
export function getProviderCapabilities(type: string): ProviderCapabilities { switch (type) { case 'claude': - return CLAUDE_CAPABILITIES; + return { ...CLAUDE_CAPABILITIES }; case 'codex': - return CODEX_CAPABILITIES; + return { ...CODEX_CAPABILITIES }; default: throw new UnknownProviderError(type, [...REGISTERED_PROVIDERS]); } }📝 Committable suggestion
🤖 Prompt for AI Agents