buildSecretRedactor function
Assembles the startup SecretRedactor: the API keys this CLI knows
about (every catalog provider's env names, the web-search slots) are
masked from tool results and the provider context so they cannot leak
into the LLM conversation or the session files. The rotation stacks
collected for the roles resolver and the values preloaded from the
platform secure store (keychain values must never reach the transcript
either) are redacted too. queueSecrets (gh-1781) registers every
resolved provider-queue credential — queue entries routinely carry
CUSTOM env var names (COPILOT_EPAM_KEY, ZAI_CODE_KEY, …) that no
catalog list knows, so the values must ride the redaction explicitly.
The spawned shell already inherits the process environment, so no env
injection is needed here.
Implementation
SecretRedactor buildSecretRedactor({
Map<String, String> roleSecrets = const {},
Map<String, String> queueSecrets = const {},
SecureKeyCache? keys,
Map<String, String>? env,
RedactionPipeline? pipeline,
}) {
final redactor = SecretRedactor();
// Every secret this function registers into the legacy exact-value
// redactor ALSO feeds the layered pipeline's registered layer, so both
// masking systems stay in sync (issue #24 stage 3).
void registerBoth(String name, String value) {
redactor.register(name, value);
pipeline?.registerSecret(value);
}
final environment = env ?? Platform.environment;
for (final name in [
for (final spec in providerCatalog.values) ...spec.apiKeyEnvNames,
'BRAVE_API_KEY',
'TAVILY_API_KEY',
]) {
final value = environment[name];
if (value != null) registerBoth(name, value);
}
for (final entry in roleSecrets.entries) {
registerBoth(entry.key, entry.value);
}
for (final entry in queueSecrets.entries) {
registerBoth(entry.key, entry.value);
}
if (keys != null) {
for (final name in keys.names) {
final value = keys.read(name);
if (value != null) registerBoth(name, value);
}
}
return redactor;
}