#3150 reworded write_file's description, overwrite parameter, and guard
error to prefer append_file/edit_file. But those tools register via their
own enable flags and the per-agent allowlist, so a config exposing only
write_file was steered toward tools it does not have — a dead end in the
most restrictive setups.
WriteFileTool now tracks which non-destructive alternatives are available
and names only the ones present; when none are, the guard still blocks the
overwrite without pointing anywhere. The agent wiring registers the editors
before write_file and resolves availability via registry.HasRegistered,
which reflects both the enable flag and the allowlist.
Tests: conditional copy at the tool level (none/one/both) and agent wiring
across the enable-flag and allowlist dimensions.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address review feedback: the routed-agent /clear path bypassed the
public ContextManager.Clear contract via an unexported hook. Restore
the single Clear call in the command path and resolve the session's
owning agent inside the built-in implementations instead:
- legacy: Clear resolves the owning agent via agentForSession instead
of assuming the default agent
- seahorse: drop ClearContextStore; Clear wipes the engine state and
the owning agent's session store
- command path: persist session scope metadata before Clear so
ownership resolves even when /clear is the session's first message
- add coverage that a custom ContextManager receives Clear for routed
agents
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Previously when io.Copy to the base64 encoder failed, encoder.Close()
was skipped. This left the encoder's internal buffer unflushed.
Now always call Close() and handle both copy and close errors
explicitly.
Two bugs prevented the usage block from ever reaching the wire:
1. CallLLM read turnStateFromContext(ctx), but the raw ctx is not seeded
with the turn state (only turnCtx is), so SetLastUsage/SetLastFinishReason
were dropped — GetLastUsage() returned nil at finalize. Set them on the
ts parameter directly, which is also what the streaming publisher reads.
2. The manager wraps the channel streamer in finalizeHookStreamer /
splitMarkerStreamer, neither of which forwarded SetTurnUsage (it is not
part of the bus.Streamer interface), so the type assertion in the
publisher's Finalize failed silently. Mirror the existing SetModelName
forwarding: add a turnUsageStreamer interface + setStreamerTurnUsage
helper and SetTurnUsage methods on both wrappers (splitMarker also stores
and re-applies usage to each freshly-begun part streamer).
Adds regression tests asserting both wrappers forward SetTurnUsage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two concurrency bugs identified in PR #2904 review:
1. Replace sync.WaitGroup with sync.Cond-based activeReqCount to avoid
the "WaitGroup is reused before previous Wait has returned" panic that
occurs when Add(1) races with a goroutine-launched Wait().
2. Make panic cleanup conditional: when runTurn panics, only delete the
session's activeTurnStates entry if it still points to our placeholder.
Previously, an unconditional delete could wipe a new message's slot
claimed between the panic and the deferred cleanup.
Replace raw log.Printf and fmt.Printf calls in pkg/state, pkg/agent, and pkg/tools with structured logger calls (WarnCF/InfoCF). This ensures warnings and info messages are routed through the configured logging infrastructure instead of raw stderr/stdout.
Add a warning log when the type assertion from sync.Map.LoadAndDelete fails in UnsubscribeEvents, per review suggestion. This makes a mismatched type observable for debugging.
Address remaining review feedback: 1) Add HistoryTokens field to ContextUsage/ContextStats, showing history-only token count in /context and frontend UI alongside SummarizeAtTokens so users can see the actual summarization trigger comparison. 2) Remove .codebuddy/github-contribute/ state files accidentally included in the PR.
sync.Map.LoadAndDelete returns any; unprotected type assertion could panic if an unexpected type were stored. Add ok check to safely handle mismatched types.
The /context command previously showed only the hard budget compression
threshold (contextWindow - maxTokens), which confused users who expected
to see the soft summarization trigger from summarize_token_percent.
This commit adds SummarizeAtTokens alongside the existing CompressAtTokens
so that both thresholds are visible:
- Compress at: contextWindow - maxTokens (hard budget, triggers proactive
compression when exceeded)
- Summarize at: contextWindow * summarizeTokenPercent / 100 (soft trigger,
matches maybeSummarize's threshold)
The fix updates the /context command output, the Web UI popover, and the
pico channel WebSocket payload.
Fixes#2968
Previously, only timeout and network errors (matched via string
patterns) were retried. HTTP 500 server errors from
OpenRouter/OpenAI-compatible providers would fail the agent turn
immediately when no model fallback candidate was available.
This commit replaces the separate timeout/network retry branches
with a unified transientLLMRetryReason() helper that:
1. Uses providers.ClassifyError() to detect server_error (HTTP >=500),
timeout, network, and rate_limit errors
2. Falls back to the existing string-based detection for errors
not classified by the provider
A regression test (TestPipeline_CallLLM_HTTP5xxRetry) verifies that
HTTP 500 errors are retried and recover successfully.
This is a clean rebase of the approach originally proposed in #2768
by afjcjsbx.
- Persistence layer (jsonl.go addMsg/SetHistory) normalizes CreatedAt
when missing so the invariant is guaranteed at the storage boundary
- API layer (session.go) exposes created_at on all transcript message
types with session.updated fallback for legacy messages
- Frontend uses per-message timestamps when available
- messagesContentEqual ignores CreatedAt for tail-matching after
JSONL roundtrip
Fixes#2787
* feat: add request-scoped context policies
Add named turn profiles under agents.defaults so callers can opt into
per-request context and tool policies without changing default chat behavior.
Profiles can disable history, system context, skill prompts, or tools, and can
limit skills/tools with allow lists. Wire profile selection through Pico message
payloads, agent turn execution, Web chat selection, and Web visual config.
Reject invalid turn profiles before saving config through Web APIs and document
the new request context policy behavior.
* fix: address turn profile review blockers
* feat: simplify request context policy config
* fix: suppress tool prompt when turn tools are disabled
* fix: enforce turn profile tool restrictions
* feat(chat,seahorse): persist and display model_name across history
* test(seahorse): fix lint regressions in repair coverage
* fix(pico): preserve model_name in live updates
* fix(pico): preserve model_name through live stream wrappers
* Support streaming
* fix: stream pico reasoning updates
Route Pico reasoning through the active streamer and hide empty thought placeholders.
* fix: harden configured streaming delivery
* fix ci
* fix split issue