Add auth mode, fix MCP token handling, and parallelize startup loading

Auth mode: when a tool requires an API key, the thread enters a special
mode where the next user message is routed directly to the credential
store, bypassing logs, turns, history, and compaction entirely. This
prevents tokens from leaking into debug output or persistent storage.

Fix MCP auth: auth_mcp now actually uses the token parameter (was
ignored as _token) and falls back to manual token entry when OAuth
and DCR are both unsupported.

Parallel loading: WASM tools, WASM channels, and MCP servers now load
concurrently at startup. Within each loader, individual items also
load in parallel (join_all for WASM, JoinSet for MCP servers).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
Illia Polosukhin
2026-02-06 13:35:17 -08:00
co-authored by Claude Opus 4.6
parent 9b729795fb
commit 2cdd04a359
7 changed files with 533 additions and 142 deletions
+15 -9
View File
@@ -165,17 +165,18 @@ impl WasmToolLoader {
}
let mut results = LoadResults::default();
// Collect all .wasm entries first, then load in parallel
let mut tool_entries = Vec::new();
let mut entries = fs::read_dir(dir).await?;
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
// Only process .wasm files
if path.extension().and_then(|e| e.to_str()) != Some("wasm") {
continue;
}
// Extract tool name from filename
let name = match path.file_stem().and_then(|s| s.to_str()) {
Some(n) => n.to_string(),
None => {
@@ -187,15 +188,20 @@ impl WasmToolLoader {
}
};
// Look for sidecar capabilities file
let cap_path = path.with_extension("capabilities.json");
let cap_path_option = if cap_path.exists() {
Some(cap_path.as_path())
} else {
None
};
let has_cap = cap_path.exists();
tool_entries.push((name, path, if has_cap { Some(cap_path) } else { None }));
}
match self.load_from_files(&name, &path, cap_path_option).await {
// Load all tools in parallel (file I/O + WASM compilation + registration)
let load_futures = tool_entries
.iter()
.map(|(name, path, cap_path)| self.load_from_files(name, path, cap_path.as_deref()));
let load_results = futures::future::join_all(load_futures).await;
for ((name, path, _), result) in tool_entries.into_iter().zip(load_results) {
match result {
Ok(()) => {
results.loaded.push(name);
}