fix: subagent can't created in opencode free model /login no apikey
Browse files- .gitignore +1 -0
- src/utils/auth.ts +3 -3
- src/utils/model/agent.ts +30 -1
- src/utils/swarm/spawnUtils.ts +13 -0
.gitignore
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
node_modules/
|
| 2 |
dist/
|
| 3 |
cli
|
|
|
|
| 4 |
VersperClaw
|
|
|
|
| 1 |
node_modules/
|
| 2 |
dist/
|
| 3 |
cli
|
| 4 |
+
.codex
|
| 5 |
VersperClaw
|
src/utils/auth.ts
CHANGED
|
@@ -307,7 +307,7 @@ export function getOpenRouterApiKeyWithSource(): {
|
|
| 307 |
: { key: null, source: 'none' }
|
| 308 |
}
|
| 309 |
|
| 310 |
-
export function getConfiguredAuthProvider(): 'anthropic' | 'openrouter' | 'openai' | 'local' | null {
|
| 311 |
// First try to get from cache for performance
|
| 312 |
const storedProvider = getGlobalConfig().authProvider
|
| 313 |
if (storedProvider) {
|
|
@@ -319,13 +319,13 @@ export function getConfiguredAuthProvider(): 'anthropic' | 'openrouter' | 'opena
|
|
| 319 |
}
|
| 320 |
|
| 321 |
// Read authProvider directly from file to bypass cache
|
| 322 |
-
export function getConfiguredAuthProviderFromFile(): 'anthropic' | 'openrouter' | 'openai' | 'local' | null {
|
| 323 |
try {
|
| 324 |
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
| 325 |
const { readFileSync } = require('fs') as typeof import('fs')
|
| 326 |
const raw = readFileSync(getGlobalClaudeFile(), 'utf8')
|
| 327 |
const config = JSON.parse(raw) as {
|
| 328 |
-
authProvider?: 'anthropic' | 'openrouter' | 'openai' | 'local'
|
| 329 |
}
|
| 330 |
|
| 331 |
if (config.authProvider) {
|
|
|
|
| 307 |
: { key: null, source: 'none' }
|
| 308 |
}
|
| 309 |
|
| 310 |
+
export function getConfiguredAuthProvider(): 'anthropic' | 'openrouter' | 'openai' | 'local' | 'opencode' | null {
|
| 311 |
// First try to get from cache for performance
|
| 312 |
const storedProvider = getGlobalConfig().authProvider
|
| 313 |
if (storedProvider) {
|
|
|
|
| 319 |
}
|
| 320 |
|
| 321 |
// Read authProvider directly from file to bypass cache
|
| 322 |
+
export function getConfiguredAuthProviderFromFile(): 'anthropic' | 'openrouter' | 'openai' | 'local' | 'opencode' | null {
|
| 323 |
try {
|
| 324 |
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
| 325 |
const { readFileSync } = require('fs') as typeof import('fs')
|
| 326 |
const raw = readFileSync(getGlobalClaudeFile(), 'utf8')
|
| 327 |
const config = JSON.parse(raw) as {
|
| 328 |
+
authProvider?: 'anthropic' | 'openrouter' | 'openai' | 'local' | 'opencode'
|
| 329 |
}
|
| 330 |
|
| 331 |
if (config.authProvider) {
|
src/utils/model/agent.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
| 8 |
parseUserSpecifiedModel,
|
| 9 |
} from './model.js'
|
| 10 |
import { getAPIProvider } from './providers.js'
|
|
|
|
| 11 |
|
| 12 |
export const AGENT_MODEL_OPTIONS = [...MODEL_ALIASES, 'inherit'] as const
|
| 13 |
export type AgentModelAlias = (typeof AGENT_MODEL_OPTIONS)[number]
|
|
@@ -48,6 +49,14 @@ export function getAgentModel(
|
|
| 48 |
// This ensures subagents use the same cross-region inference profile (e.g., "eu.", "us.")
|
| 49 |
// as the parent, which is required when IAM permissions only allow specific regions.
|
| 50 |
const parentRegionPrefix = getBedrockRegionPrefix(parentModel)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
// Helper to apply parent region prefix for Bedrock models.
|
| 53 |
// `originalSpec` is the raw model string before resolution (alias or full ID).
|
|
@@ -59,15 +68,31 @@ export function getAgentModel(
|
|
| 59 |
resolvedModel: string,
|
| 60 |
originalSpec: string,
|
| 61 |
): string => {
|
| 62 |
-
if (parentRegionPrefix &&
|
| 63 |
if (getBedrockRegionPrefix(originalSpec)) return resolvedModel
|
| 64 |
return applyBedrockRegionPrefix(resolvedModel, parentRegionPrefix)
|
| 65 |
}
|
| 66 |
return resolvedModel
|
| 67 |
}
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
// Prioritize tool-specified model if provided
|
| 70 |
if (toolSpecifiedModel) {
|
|
|
|
|
|
|
|
|
|
| 71 |
if (aliasMatchesParentTier(toolSpecifiedModel, parentModel)) {
|
| 72 |
return parentModel
|
| 73 |
}
|
|
@@ -87,6 +112,10 @@ export function getAgentModel(
|
|
| 87 |
})
|
| 88 |
}
|
| 89 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
if (aliasMatchesParentTier(agentModelWithExp, parentModel)) {
|
| 91 |
return parentModel
|
| 92 |
}
|
|
|
|
| 8 |
parseUserSpecifiedModel,
|
| 9 |
} from './model.js'
|
| 10 |
import { getAPIProvider } from './providers.js'
|
| 11 |
+
import { getOpenCodeApiKey } from '../auth.js'
|
| 12 |
|
| 13 |
export const AGENT_MODEL_OPTIONS = [...MODEL_ALIASES, 'inherit'] as const
|
| 14 |
export type AgentModelAlias = (typeof AGENT_MODEL_OPTIONS)[number]
|
|
|
|
| 49 |
// This ensures subagents use the same cross-region inference profile (e.g., "eu.", "us.")
|
| 50 |
// as the parent, which is required when IAM permissions only allow specific regions.
|
| 51 |
const parentRegionPrefix = getBedrockRegionPrefix(parentModel)
|
| 52 |
+
const provider = getAPIProvider()
|
| 53 |
+
|
| 54 |
+
// OpenCode free-mode sessions are known to be more reliable when subagents
|
| 55 |
+
// stay on the parent's already-working model instead of switching to alias-
|
| 56 |
+
// derived variants like haiku -> gpt-5-nano. If the user has configured an
|
| 57 |
+
// OpenCode API key, keep honoring explicit alias-based model selection.
|
| 58 |
+
const shouldInheritParentForKeylessOpenCode =
|
| 59 |
+
provider === 'opencode' && !getOpenCodeApiKey()
|
| 60 |
|
| 61 |
// Helper to apply parent region prefix for Bedrock models.
|
| 62 |
// `originalSpec` is the raw model string before resolution (alias or full ID).
|
|
|
|
| 68 |
resolvedModel: string,
|
| 69 |
originalSpec: string,
|
| 70 |
): string => {
|
| 71 |
+
if (parentRegionPrefix && provider === 'bedrock') {
|
| 72 |
if (getBedrockRegionPrefix(originalSpec)) return resolvedModel
|
| 73 |
return applyBedrockRegionPrefix(resolvedModel, parentRegionPrefix)
|
| 74 |
}
|
| 75 |
return resolvedModel
|
| 76 |
}
|
| 77 |
|
| 78 |
+
const shouldStickToParentModel = (spec: string): boolean => {
|
| 79 |
+
if (!shouldInheritParentForKeylessOpenCode) {
|
| 80 |
+
return false
|
| 81 |
+
}
|
| 82 |
+
const normalized = spec.toLowerCase()
|
| 83 |
+
return (
|
| 84 |
+
normalized === 'haiku' ||
|
| 85 |
+
normalized === 'sonnet' ||
|
| 86 |
+
normalized === 'opus' ||
|
| 87 |
+
normalized === 'best'
|
| 88 |
+
)
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
// Prioritize tool-specified model if provided
|
| 92 |
if (toolSpecifiedModel) {
|
| 93 |
+
if (shouldStickToParentModel(toolSpecifiedModel)) {
|
| 94 |
+
return parentModel
|
| 95 |
+
}
|
| 96 |
if (aliasMatchesParentTier(toolSpecifiedModel, parentModel)) {
|
| 97 |
return parentModel
|
| 98 |
}
|
|
|
|
| 112 |
})
|
| 113 |
}
|
| 114 |
|
| 115 |
+
if (shouldStickToParentModel(agentModelWithExp)) {
|
| 116 |
+
return parentModel
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
if (aliasMatchesParentTier(agentModelWithExp, parentModel)) {
|
| 120 |
return parentModel
|
| 121 |
}
|
src/utils/swarm/spawnUtils.ts
CHANGED
|
@@ -94,6 +94,10 @@ export function buildInheritedCliFlags(options?: {
|
|
| 94 |
* parent's env, so we forward any that are set in the current process.
|
| 95 |
*/
|
| 96 |
const TEAMMATE_ENV_VARS = [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
// API provider selection — without these, teammates default to firstParty
|
| 98 |
// and send requests to the wrong endpoint (GitHub issue #23561)
|
| 99 |
'CLAUDE_CODE_USE_BEDROCK',
|
|
@@ -121,6 +125,15 @@ const TEAMMATE_ENV_VARS = [
|
|
| 121 |
'http_proxy',
|
| 122 |
'NO_PROXY',
|
| 123 |
'no_proxy',
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
'SSL_CERT_FILE',
|
| 125 |
'NODE_EXTRA_CA_CERTS',
|
| 126 |
'REQUESTS_CA_BUNDLE',
|
|
|
|
| 94 |
* parent's env, so we forward any that are set in the current process.
|
| 95 |
*/
|
| 96 |
const TEAMMATE_ENV_VARS = [
|
| 97 |
+
// Explicit provider override — without these, spawned teammates can fall
|
| 98 |
+
// back to firstParty even when the leader is pinned to OpenCode/OpenAI/etc.
|
| 99 |
+
'BETTER_CLAWD_API_PROVIDER',
|
| 100 |
+
'CLAUDE_CODE_API_PROVIDER',
|
| 101 |
// API provider selection — without these, teammates default to firstParty
|
| 102 |
// and send requests to the wrong endpoint (GitHub issue #23561)
|
| 103 |
'CLAUDE_CODE_USE_BEDROCK',
|
|
|
|
| 125 |
'http_proxy',
|
| 126 |
'NO_PROXY',
|
| 127 |
'no_proxy',
|
| 128 |
+
// Third-party provider endpoints / auth overrides that may be configured
|
| 129 |
+
// only in the parent shell environment.
|
| 130 |
+
'OPENAI_API_KEY',
|
| 131 |
+
'OPENAI_ACCESS_TOKEN',
|
| 132 |
+
'CODEX_ACCESS_TOKEN',
|
| 133 |
+
'OPENROUTER_API_KEY',
|
| 134 |
+
'OPENAI_BASE_URL',
|
| 135 |
+
'OPENROUTER_BASE_URL',
|
| 136 |
+
'OPENCODE_BASE_URL',
|
| 137 |
'SSL_CERT_FILE',
|
| 138 |
'NODE_EXTRA_CA_CERTS',
|
| 139 |
'REQUESTS_CA_BUNDLE',
|