feat(engine): the manager — chat orders, team, triage, verdicts, bootstrap
Phase 3 of the AI-workforce engine (TODO-agent-2): - engine/manager.py: ManagerTurns.chat (CREATE_TASK/PAUSE/RESUME/CANCEL, HIRE/UPDATE_EMPLOYEE/FIRE, KNOWLEDGE), triage (ASSIGN/REJECT/ASK_USER), final_review (COMPLETE/REVISE +cycle), error_review (RESOLVE_ERROR/CANCEL), bootstrap_team; post_user_message now delegates to it. - One billing path: call_model/BillingScope in runner.py is the single model entry point (fixes the agybot chat-billing bug). - Team changes capped at 3/turn, employee cap, last manager cannot be fired, hires pick a granted model of the requested tier strictly. - container_engine.py wires the real WorkforceBootstrapPort. - Fixes: REVISE is a body tag (prompt and parser now agree); ASSIGN employee="" means pick-for-me; the runner's protocol-error retry is capped by engine_max_protocol_retries (was unbounded until the turn timeout). - 20 new manager tests + regression tests; report in docs/plan/reports/agent-2-phase-3.md.
This commit is contained in:
@@ -407,6 +407,33 @@ async def test_the_tool_call_cap_stops_a_runaway_turn(_db, session_factory, monk
|
||||
assert result.error_kind == "protocol"
|
||||
|
||||
|
||||
async def test_a_model_that_repeats_its_protocol_mistake_gives_up(
|
||||
_db, session_factory, monkeypatch
|
||||
):
|
||||
"""A malformed reply is retried, but not forever.
|
||||
|
||||
The regression: a model that answers with a tag its role may not use got
|
||||
the error quoted back and another chance, with no cap — so a model that
|
||||
repeats itself burned paid calls until the 900 s turn timeout.
|
||||
"""
|
||||
monkeypatch.setattr(settings, "engine_max_protocol_retries", 2)
|
||||
# A tag the developer may not use: refused every single time.
|
||||
scripted = ScriptedModel([ScriptedReply(content="[[ASSIGN employee=\"سارا\"/]]")] * 9)
|
||||
async with session_factory() as session:
|
||||
runner = _runner(
|
||||
session, model=scripted, sandbox=FakeSandbox(), grants=FakeGrants([_db.model_id])
|
||||
)
|
||||
result = await runner.run_turn(await _request(_db, **DEVELOPER))
|
||||
await session.commit()
|
||||
|
||||
assert result.ok is False
|
||||
assert result.error_kind == "protocol"
|
||||
assert result.incomplete is True
|
||||
# 1 malformed reply + 2 retries, then it stops: never the whole script.
|
||||
assert result.model_calls == 3
|
||||
assert scripted.calls == 3
|
||||
|
||||
|
||||
async def test_the_turn_timeout_stops_a_slow_loop(_db, session_factory, monkeypatch):
|
||||
monkeypatch.setattr(settings, "engine_turn_timeout_seconds", 0)
|
||||
scripted = ScriptedModel([ScriptedReply(content='[[LIST_DIR path="."/]]')] * 3)
|
||||
|
||||
Reference in New Issue
Block a user