core+web: agent-tagged log prefixes; one-shot capability hints; v0.9.50 - #196
Conversation
Log lines emitted inside a run now carry the running agent's name: CURRENT_AGENT (lovia.log_config) is set by RunLoop for the run's duration (updated on handoff, restored on exit), and the web run-source filter composes it with the run's source into [user/lovia], [user/memory-digest], [followups]-style prefixes — a side-run a chat triggers is no longer indistinguishable from the chat itself. The two static capability hints — 'context.window: unknown' and the returns_images 'not offering it' gate — now log once per process per model instead of on every run bootstrap: each helper side-run (follow-ups, titles, memory curation) repeated them after every chat exchange. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves log attribution and reduces repetitive bootstrap diagnostics in lovia (core + lovia web) by (1) tagging logs with the currently-running agent name and (2) logging certain static capability/context-window hints only once per process per model. It also bumps the project version to 0.9.50.
Changes:
- Add
lovia.log_config.CURRENT_AGENT(contextvar) and set/reset it inRunLoop.stream()(and re-set on activation) so logs can be attributed to the active agent across handoffs and nested runs. - Compose web
RUN_SOURCE+CURRENT_AGENTinto a single%(run_source)sprefix (e.g.[user/lovia],[user/memory-digest],[followups]) for clearer console logs. - Introduce a loop-level
_log_oncememo to avoid repeating static “context.window unknown” / “tool … not offering it” hints across multiple runs; add tests for both behaviors.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Bump locked package version to 0.9.50. |
| pyproject.toml | Bump project version to 0.9.50. |
| lovia/init.py | Bump __version__ to 0.9.50. |
| lovia/log_config.py | Add CURRENT_AGENT contextvar for per-task agent tagging. |
| lovia/runtime/loop.py | Set/reset CURRENT_AGENT during runs; add _log_once for one-shot hints. |
| lovia/web/supervisor.py | Update run-source log filter to include agent tag in prefixes. |
| lovia/web/main.py | Update logging-format comment to reflect composed prefixes. |
| tests/workspace/test_workspace_agent.py | Add test asserting image-tool gating hint logs once per model. |
| tests/web/test_subagents.py | Extend tests to cover composed [source/agent] and agent-only tags. |
| tests/runtime/test_loop.py | Add tests for CURRENT_AGENT set/restore and nested-run shadowing. |
| tests/runtime/test_context_window_discovery.py | Add test asserting unknown context-window hint logs once per model. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| def filter(self, record: logging.LogRecord) -> bool: | ||
| source = RUN_SOURCE.get() | ||
| record.run_source = f"[{source}] " if source else "" | ||
| tag = "/".join(p for p in (RUN_SOURCE.get(), CURRENT_AGENT.get()) if p) | ||
| record.run_source = f"[{tag}] " if tag else "" | ||
| return True |
There was a problem hiding this comment.
Fixed in 3d062d7: the filter collapses control characters in the composed tag before formatting (isprintable() fast path, so the per-record cost stays a single check). Bracket characters are left alone — they can't fabricate lines, only look odd.
A free-form agent name containing a newline would split the one-line [source/agent] prefix and fabricate log lines. isprintable() keeps the per-record cost to a fast check; the collapse runs only on a pathological name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Keep the cross-module contract notes (CURRENT_AGENT set by the loop, read by the web filter; the suppress(ValueError) edge) and drop restated rationale. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
What
Follow-up to the log investigation: two changes that make
lovia webconsole logs attributable and quiet.Agent-tagged log prefixes
Every log line emitted inside a run now names the agent it belongs to. A new core contextvar
lovia.log_config.CURRENT_AGENTis set byRunLoop.stream()for the run's duration (re-set on handoff in_resolve_active, restored when the run ends; nested side-runs shadow and restore the host's tag). The web run-source filter composes it with the existing run source:[user][user/lovia][user][user/memory-digest][followups]/[titler][subagent:abc][subagent:abc/researcher]This is what made the original confusion possible: the memory digest's
context.window: unknownline rendered as plain[user]right after the chat'srun.done, reading as if the chat run had lost its configured window.One-shot capability hints
context.window: unknown for ...andtool 'view_image' ... not offering itare static facts about a model, not per-run events — but they re-fired on every bootstrap, including every follow-up/title/memory side-run. Both now log once per process per model (_log_oncememo in the loop), keeping the first occurrence's diagnostic value at INFO.Deliberately not done
Threading the configured context window into helper side-runs: one-shot runs have no history to compact (proactive budgeting is a no-op; reactive overflow rescue works without a window), and the correct wiring is not small — the followup/title model can be a different endpoint than the default profile, and the memory plugin is core so it cannot see web config. Assessment in the session; can be a follow-up if wanted.
Tests
CURRENT_AGENTset during a run, restored after; nested run shadows and restoresFull suite: 2310 passed, 41 skipped.
🤖 Generated with Claude Code