mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-02 14:28:59 +02:00
[eric] release: 1.7.9 ships with a story, and two log assertions stop reading the root logger
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018foyDoK19jjbYdudfzQVkZ
This commit is contained in:
co-authored by
Claude Opus 5
parent
3e0e1f67c6
commit
f8d67fdfd1
@@ -21,6 +21,28 @@ P_RELEASES: List[ReleaseNote] = [
|
||||
# Only lines that are true of the built app belong here. This one file feeds the in-app card, the
|
||||
# GitHub body AND the Help agent's context, so a line written for a planned feature becomes the
|
||||
# agent confidently describing something that does not exist.
|
||||
ReleaseNote(
|
||||
version="1.7.9",
|
||||
headline="Agents keep going when the connection does not, and the fn key finally works.",
|
||||
highlights=[
|
||||
"Press fn to dictate on any Mac. It asks for permission the first time you use it rather than at install, and it recovers without a relaunch if you grant it later.",
|
||||
"Drop files straight onto a chat. Anything the app cannot take now says so instead of silently doing nothing.",
|
||||
"The transcript follows a running agent until you scroll away, and re-pins itself the moment you scroll back.",
|
||||
"Agents can move, tile, collapse and close their own cards, so a busy canvas tidies itself.",
|
||||
],
|
||||
fixes=[
|
||||
"A dropped connection no longer costs you the conversation. The agent reconnects and carries on from where it was.",
|
||||
"Pressing Stop stops it. A background watchdog can no longer restart a chat you just ended.",
|
||||
"Chats stopped disconnecting mid task. A slow web page was being mistaken for a jammed tool and shot, roughly ten times a week per install.",
|
||||
"An agent that runs out of things to say gets asked once for its answer instead of leaving you looking at a Done label with nothing under it.",
|
||||
"Signing in on a fresh install works. The hand off back from your browser was landing on the wrong port.",
|
||||
"An expired provider login renews itself mid run instead of stopping the turn and asking you to reconnect.",
|
||||
"A browser agent can no longer report a task complete when the page never changed.",
|
||||
"Turning a schedule on or off tells you the truth. A failed save used to leave the switch showing the wrong state.",
|
||||
"Card resizing ends when you let go, even over an embedded browser.",
|
||||
"Dictation goes to the chat you were typing in, not a brand new one.",
|
||||
],
|
||||
),
|
||||
ReleaseNote(
|
||||
version="1.7.8",
|
||||
headline="A stuck chat unsticks itself, and agents can finally upload files.",
|
||||
|
||||
@@ -19,6 +19,38 @@ from backend.apps.agents.core.error_classify import (
|
||||
)
|
||||
|
||||
TURN_RUNNER = "backend/apps/agents/manager/run/TurnRunner.py"
|
||||
|
||||
class p_capture:
|
||||
"""Capture a specific logger directly.
|
||||
|
||||
`caplog` reads the ROOT logger, so it goes blind the moment any other test in the run sets
|
||||
propagate=False on ours: these two assertions passed alone and failed in the full suite while
|
||||
the line was plainly there in captured stderr. A logging assertion must not depend on what
|
||||
3,000 other tests did to the logging tree.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
import logging
|
||||
self.logger = logging.getLogger(name)
|
||||
self.records = []
|
||||
self.handler = logging.Handler()
|
||||
self.handler.emit = lambda r: self.records.append(r)
|
||||
|
||||
def __enter__(self):
|
||||
import logging
|
||||
self.prev = self.logger.level
|
||||
self.logger.setLevel(logging.WARNING)
|
||||
self.logger.addHandler(self.handler)
|
||||
return self
|
||||
|
||||
def __exit__(self, *a):
|
||||
self.logger.removeHandler(self.handler)
|
||||
self.logger.setLevel(self.prev)
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return "\n".join(r.getMessage() for r in self.records)
|
||||
|
||||
# Where each fault is raised. A fault this build knows but wires nowhere is the exact shape of a
|
||||
# guard that never fires, so the map is the test, not a convenience.
|
||||
WIRED_IN = {
|
||||
@@ -109,18 +141,17 @@ def test_every_known_fault_is_wired_somewhere():
|
||||
assert p_block(kind).strip(), f"{kind} has an empty branch"
|
||||
|
||||
|
||||
def test_arming_is_announced_and_a_typo_is_named(monkeypatch, caplog):
|
||||
def test_arming_is_announced_and_a_typo_is_named(monkeypatch):
|
||||
# The harness built to kill row-6 silence had it: unknown_faults() existed and NOTHING called it,
|
||||
# so a mistyped name armed nothing while the drill exercised the untouched happy path.
|
||||
monkeypatch.setenv("OSW_FAULT", "policy_block,plicy_blok")
|
||||
with caplog.at_level("WARNING"):
|
||||
with p_capture("backend.apps.agents.core.fault_injection") as cap:
|
||||
announce()
|
||||
assert "policy_block" in caplog.text and "plicy_blok" in caplog.text
|
||||
caplog.clear()
|
||||
assert "policy_block" in cap.text and "plicy_blok" in cap.text
|
||||
monkeypatch.delenv("OSW_FAULT")
|
||||
with caplog.at_level("WARNING"):
|
||||
with p_capture("backend.apps.agents.core.fault_injection") as cap2:
|
||||
announce()
|
||||
assert caplog.text == "", "a shipped build must say nothing at all"
|
||||
assert cap2.text == "", "a shipped build must say nothing at all"
|
||||
|
||||
|
||||
def test_the_boot_path_actually_announces():
|
||||
|
||||
@@ -118,12 +118,13 @@ def test_the_hook_shapes_the_pristine_response_not_the_flattened_one():
|
||||
assert i_capture < i_use
|
||||
|
||||
|
||||
def test_the_off_switch_is_declared_and_announces_itself(monkeypatch, caplog):
|
||||
def test_the_off_switch_is_declared_and_announces_itself(monkeypatch):
|
||||
from backend.apps.agents.manager.streaming import tool_output_shaper as mod
|
||||
from backend.tests.test_fault_injection import p_capture
|
||||
|
||||
class S:
|
||||
id = "s1"
|
||||
monkeypatch.setenv("OSW_TOOL_SHAPING", "off")
|
||||
with caplog.at_level("WARNING"):
|
||||
with p_capture("backend.apps.agents.manager.streaming.tool_output_shaper") as cap:
|
||||
assert mod.shape_for_model(S(), "s1", {"stdout": p_big()}, "m1", "Bash") is None
|
||||
assert "OFF" in caplog.text, "a guard that stops guarding must say which sessions it stopped protecting"
|
||||
assert "OFF" in cap.text, "a guard that stops guarding must say which sessions it stopped protecting"
|
||||
|
||||
Reference in New Issue
Block a user