fix(api): forked sessions carry _branched_from so they stay listable
Review finding: with the child now created before the parent is ended, child.started_at < parent.ended_at, so _BRANCH_CHILD_SQL's timestamp fallback no longer classifies the fork as a branch child and the default GET /api/sessions dropped it. Persist the explicit marker the CLI /branch path already writes; test covers listing + the failed-fork parent survival.
This commit is contained in:
@@ -2982,9 +2982,13 @@ class APIServerAdapter(OpenAICompatRoutesMixin, BasePlatformAdapter):
|
||||
|
||||
# CLI /branch semantics: create the child, then end the original as branched (child first, so
|
||||
# a failed create never leaves the source ended with no fork, #11030).
|
||||
# ``_branched_from`` is the durable branch marker (same as CLI /branch): with the child created
|
||||
# first, the timestamp fallback in _BRANCH_CHILD_SQL (child.started_at >= parent.ended_at) no
|
||||
# longer holds, and an unmarked child would vanish from default session listings.
|
||||
await asyncio.to_thread(
|
||||
db.create_session, fork_id, "api_server", model=source.get("model"),
|
||||
system_prompt=source.get("system_prompt"), parent_session_id=source_id)
|
||||
system_prompt=source.get("system_prompt"), parent_session_id=source_id,
|
||||
model_config={"_branched_from": source_id})
|
||||
await asyncio.to_thread(db.end_session, source_id, "branched")
|
||||
messages = await asyncio.to_thread(db.get_messages, source_id)
|
||||
await asyncio.to_thread(db.replace_messages, fork_id, messages)
|
||||
|
||||
@@ -116,6 +116,25 @@ async def test_session_messages_default_to_latest_bounded_page(adapter, session_
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_forked_session_stays_listable_and_parent_survives_failed_fork(adapter, session_db):
|
||||
"""A fork is created before the parent is ended (#11030) and carries the explicit branch
|
||||
marker, so it still shows in the default listing (the timestamp fallback no longer holds)."""
|
||||
session_db.create_session("parent", "api_server")
|
||||
app = _create_session_app(adapter)
|
||||
async with TestClient(TestServer(app)) as cli:
|
||||
resp = await cli.post("/api/sessions/parent/fork", json={"id": "child"})
|
||||
assert resp.status == 201
|
||||
listed = await (await cli.get("/api/sessions")).json()
|
||||
ids = {row["id"] for row in listed["data"]}
|
||||
assert {"parent", "child"} <= ids, ids
|
||||
|
||||
session_db.create_session("solo", "api_server")
|
||||
with patch.object(session_db, "create_session", side_effect=RuntimeError("boom")):
|
||||
resp = await cli.post("/api/sessions/solo/fork", json={"id": "never"})
|
||||
assert resp.status >= 500
|
||||
assert session_db.get_session("solo")["end_reason"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_agent_binds_api_session_context_for_tool_env(adapter, monkeypatch):
|
||||
"""API-server request sessions should reach tools and terminal subprocess env."""
|
||||
|
||||
Reference in New Issue
Block a user