mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-26 23:50:17 +00:00
feat: hot-activate WASM channels, channel-first prompts, unified artifact resolution (#297)
* refactor: unify WASM artifact resolution into registry/artifacts.rs Consolidate duplicated WASM find/build/install logic from 5+ files into a single src/registry/artifacts.rs module. This fixes two bugs: - registry/installer.rs now respects CARGO_TARGET_DIR (was hardcoded) - channels/wasm/bundled.rs now searches all WASM triples (was wasip2 only) Also includes: extension manager hot-activation for WASM channels, extension guidance in LLM prompts, channel manager hot-add support, webhook router channel lookup, and minor cleanups. Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: send approval prompts as messages on WASM channels (Telegram, Slack) WASM channels mapped ApprovalNeeded status to a typing indicator, so users on Telegram never saw tool approval prompts — the agent got stuck in AwaitingApproval and all subsequent messages failed with "Waiting for approval". - Intercept ApprovalNeeded in WasmChannel::handle_status_update and send the prompt as an actual message via call_on_respond, showing tool name, description, parameters, and yes/no/always instructions - Guard against empty LLM responses after clean_response() strips reasoning_content think-tags (defense-in-depth for reasoning models) - Add reasoning_content fallback to NearAiChatProvider::complete() for consistency with complete_with_tools() - Add debug logging when empty responses are suppressed - Improve error logging for channel respond() failures - Register WASM channel webhook routes before credential checks so platforms don't deactivate webhook URLs with 404s Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: address PR #297 review comments - ChannelManager::add: use async write().await instead of try_write() - resolve_target_dir: resolve relative CARGO_TARGET_DIR against crate_dir - install_wasm_files: log warning on capabilities copy failure - refresh_active_channel: load capabilities file for webhook secret name - activate_wasm_channel: validate name against path traversal - Fix cargo fmt formatting in nearai_chat.rs Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: wire up channel runtime for hot-activation and address PR review round 2 - Wire up set_channel_runtime() in main.rs so hot-activation actually works (with_channel_runtime was never called — hot-activation was dead code) - Change ExtensionManager channel runtime fields to RwLock<Option<...>> interior mutability so set_channel_runtime(&self) works after Arc wrapping - Fix artifact tests to use resolve_target_dir() instead of hardcoding "target/" (breaks when CARGO_TARGET_DIR is set) - Fix bundled.rs build hint: cargo component build (not cargo build --target) - Fix wasm_artifact_path doc: binary_name should not include .wasm extension Co-Authored-By: Claude Opus 4.6 <[email protected]> * fix: use char-aware truncation to prevent UTF-8 panic in approval prompt &s[..77] panics on multi-byte UTF-8 (CJK, emoji). Use s.chars().take(77) for safe truncation at character boundaries. Co-Authored-By: Claude Opus 4.6 <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
a320f265b3
commit
ea57447649
+36
-9
@@ -98,7 +98,7 @@ impl Agent {
|
||||
pub fn new(
|
||||
config: AgentConfig,
|
||||
deps: AgentDeps,
|
||||
channels: ChannelManager,
|
||||
channels: Arc<ChannelManager>,
|
||||
heartbeat_config: Option<HeartbeatConfig>,
|
||||
hygiene_config: Option<crate::config::HygieneConfig>,
|
||||
routine_config: Option<RoutineConfig>,
|
||||
@@ -123,7 +123,7 @@ impl Agent {
|
||||
Self {
|
||||
config,
|
||||
deps,
|
||||
channels: Arc::new(channels),
|
||||
channels,
|
||||
context_manager,
|
||||
scheduler,
|
||||
router: Router::new(),
|
||||
@@ -499,21 +499,41 @@ impl Agent {
|
||||
Ok(crate::hooks::HookOutcome::Continue {
|
||||
modified: Some(new_content),
|
||||
}) => {
|
||||
let _ = self
|
||||
if let Err(e) = self
|
||||
.channels
|
||||
.respond(&message, OutgoingResponse::text(new_content))
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
tracing::error!(
|
||||
channel = %message.channel,
|
||||
error = %e,
|
||||
"Failed to send response to channel"
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let _ = self
|
||||
if let Err(e) = self
|
||||
.channels
|
||||
.respond(&message, OutgoingResponse::text(response))
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
tracing::error!(
|
||||
channel = %message.channel,
|
||||
error = %e,
|
||||
"Failed to send response to channel"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Some(_)) => {
|
||||
Ok(Some(empty)) => {
|
||||
// Empty response, nothing to send (e.g. approval handled via send_status)
|
||||
tracing::debug!(
|
||||
channel = %message.channel,
|
||||
user = %message.user_id,
|
||||
empty_len = empty.len(),
|
||||
"Suppressed empty response (not sent to channel)"
|
||||
);
|
||||
}
|
||||
Ok(None) => {
|
||||
// Shutdown signal received (/quit, /exit, /shutdown)
|
||||
@@ -522,10 +542,17 @@ impl Agent {
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::error!("Error handling message: {}", e);
|
||||
let _ = self
|
||||
if let Err(send_err) = self
|
||||
.channels
|
||||
.respond(&message, OutgoingResponse::text(format!("Error: {}", e)))
|
||||
.await;
|
||||
.await
|
||||
{
|
||||
tracing::error!(
|
||||
channel = %message.channel,
|
||||
error = %send_err,
|
||||
"Failed to send error response to channel"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -891,7 +891,7 @@ mod tests {
|
||||
auto_approve_tools: false,
|
||||
},
|
||||
deps,
|
||||
ChannelManager::new(),
|
||||
Arc::new(ChannelManager::new()),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user