fix: harden subagent auth against keychain contention (#862)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user