fix: harden subagent auth against keychain contention (#862)

This commit is contained in:
Charles Packer
2026-02-07 19:55:09 -08:00
committed by GitHub
parent 5e654f3daa
commit 6727cd923d
3 changed files with 23 additions and 16 deletions

View File

@@ -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

View File

@@ -1372,10 +1372,17 @@ class SettingsManager {
* Check if secrets are available
*/
async isKeychainAvailable(): Promise<boolean> {
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;
}
/**

View File

@@ -218,21 +218,11 @@ export async function isKeychainAvailable(): Promise<boolean> {
}
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;