diff --git a/src/agent/subagents/manager.ts b/src/agent/subagents/manager.ts index d524fec..9dd13b4 100644 --- a/src/agent/subagents/manager.ts +++ b/src/agent/subagents/manager.ts @@ -582,10 +582,20 @@ async function executeSubagent( // Context not available } + // Resolve auth once in parent and forward to child to avoid per-subagent + // keychain lookups under high parallel fan-out. + const settings = await settingsManager.getSettingsWithSecureTokens(); + const inheritedApiKey = + process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY; + const inheritedBaseUrl = + process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL; + const proc = spawn(lettaCmd, cliArgs, { cwd: process.cwd(), env: { ...process.env, + ...(inheritedApiKey && { LETTA_API_KEY: inheritedApiKey }), + ...(inheritedBaseUrl && { LETTA_BASE_URL: inheritedBaseUrl }), // Tag Task-spawned agents for easy filtering. LETTA_CODE_AGENT_ROLE: "subagent", // Pass parent agent ID for subagents that need to access parent's context diff --git a/src/settings-manager.ts b/src/settings-manager.ts index 7cee4bb..e24d32a 100644 --- a/src/settings-manager.ts +++ b/src/settings-manager.ts @@ -1372,10 +1372,17 @@ class SettingsManager { * Check if secrets are available */ async isKeychainAvailable(): Promise { - if (this.secretsAvailable === null) { - this.secretsAvailable = await isKeychainAvailable(); + if (this.secretsAvailable === true) { + return true; } - return this.secretsAvailable; + + const available = await isKeychainAvailable(); + // Cache only positive availability to avoid pinning transient failures + // for the entire process lifetime. + if (available) { + this.secretsAvailable = true; + } + return available; } /** diff --git a/src/utils/secrets.ts b/src/utils/secrets.ts index 0fc92e8..3d50a60 100644 --- a/src/utils/secrets.ts +++ b/src/utils/secrets.ts @@ -218,21 +218,11 @@ export async function isKeychainAvailable(): Promise { } try { - // Try to set and delete a test value - const testName = "test-availability"; - const testValue = "test"; - - await secrets.set({ + // Non-mutating probe: if this call succeeds (even with null), keychain is usable. + await secrets.get({ service: SERVICE_NAME, - name: testName, - value: testValue, + name: API_KEY_NAME, }); - - await secrets.delete({ - service: SERVICE_NAME, - name: testName, - }); - return true; } catch { return false;