Two related issues with autonomous Hand agents:
1. heartbeat_interval_secs was hardcoded at 30s (the AutonomousConfig default)
for all Hands, with no way to override it from HAND.toml. For agents that
make long LLM calls, 30s causes false-positive recovery triggers during
normal operation. Add heartbeat_interval_secs to HandAgentConfig so each
Hand can declare an appropriate interval.
2. The researcher Hand shipped with max_iterations = 80 and a system prompt
instructing exhaustive research (50+ sources). This combination was designed
for cloud LLMs with 200K context windows. On any model with a 32K or smaller
context window, 80 iterations × growing history guarantees context overflow
before the task completes. Reduce to 25, which is sufficient for thorough
research within a 32K budget.
researcher/HAND.toml changes:
- max_iterations: 80 → 25
- heartbeat_interval_secs: 120 (new field; 30s default was triggering false
recovery during normal multi-minute LLM calls)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Widen agent detail modal from 600px to 700px to better accommodate
longer model names and the fallback chain editor
- Restructure the Fallbacks section in the Info tab: content div is now
a column flex container (gap:6px) with margin-left:16px to create a
clear visual column between the label and its content
- Prevent long provider/model badge strings from overflowing the right
edge of the modal (word-break:break-all; white-space:normal on badge)
- Add flex-shrink:0 to the × delete button so it never gets squashed
when a badge is long
- Wrap the "+ Add" button in a div so it stays left-aligned (column
flex would otherwise stretch a bare button to full width)
- Replace margin-top with gap-based spacing on the fallback edit form
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When agents are loaded from persistent storage on daemon startup, their
last_active timestamp reflects when they were last active before the
previous shutdown. If the daemon was down for longer than the heartbeat
timeout (default 180 s), the first heartbeat tick immediately marks every
restored agent as unresponsive and triggers crash recovery — even though
all agents just started and haven't had a chance to run.
Fix: stamp last_active = Utc::now() alongside the state = Running reset
in the restore loop. This is consistent with how new agent spawns work
(they also set last_active to now) and gives each restored agent a clean
baseline from which the heartbeat can accurately track responsiveness.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Slow local models (e.g. 27B quantised MLX models) can take 3–4+ minutes
per iteration, well beyond the default 180s heartbeat timeout. Because
last_active was only updated at the end of an iteration — never during it —
the heartbeat monitor would flag the agent as unresponsive mid-call and
initiate crash/recovery while the loop was still running correctly.
Changes:
- Add `touch()` to `AgentRegistry`: refreshes `last_active` with no other
side-effects.
- Add `touch_agent(&self, agent_id: &str)` to `KernelHandle` trait with a
default no-op, so existing mock implementations require no changes.
- Implement `touch_agent` on `OpenFangKernel`: parses the UUID and
delegates to `registry.touch()`.
- Call `kernel.touch_agent(agent_id)` at the top of each agent loop
iteration, immediately before the `call_with_retry` LLM call. This
resets the inactivity clock at the start of every iteration rather than
only at completion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tool names stored via the dashboard can arrive in any case (e.g. uppercase
FILE_READ vs registered name file_read). The previous case-sensitive
comparison caused allowlisted tools to silently match nothing, giving the
agent an empty effective tool set with no error or warning.
Normalise both sides with to_lowercase() so the filter works regardless of
how the names were entered.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The docs listed `duckduckgo` as the config value but the actual serde
deserialization produces `duck_duck_go` — serde's rename_all = "snake_case"
on the DuckDuckGo enum variant inserts underscores at each word boundary.
Updated all three occurrences in configuration.md to match the real value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add release-fast Cargo profile for faster dev builds
Introduces a `release-fast` profile that inherits from `release` but
uses thin LTO and 8 codegen units instead of full LTO + 1, cutting
link time significantly while remaining fast enough for integration
testing. Documents usage in CONTRIBUTING.md.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: allow renaming an agent to its current name
AgentRegistry::update_name was calling name_index.contains_key()
without excluding the agent being renamed. Renaming to the same name
always returned AgentAlreadyExists instead of succeeding silently.
Fix: only error when a *different* agent owns the target name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The polling loop was updating last_notification_id on every iteration,
leaving it set to the oldest (smallest) ID in the batch after the loop
completed. On the next poll, since_id was set to that oldest ID, causing
Mastodon to return all previously seen notifications again.
Re-delivered notifications caused the bot to respond to the same user
mention repeatedly. Combined with api_post_status chaining each response
chunk as a reply to the previous chunk, this produced long self-reply
threads that appeared to be the bot conversing with itself.
Fix: capture the first (newest) notification ID before processing the
batch, so since_id always advances correctly on each poll cycle.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a `release-fast` profile that inherits from `release` but
uses thin LTO and 8 codegen units instead of full LTO + 1, cutting
link time significantly while remaining fast enough for integration
testing. Documents usage in CONTRIBUTING.md.
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>