feat: enrich task process timeline events
This commit is contained in:
@ -74,9 +74,10 @@ class SessionProcessProjector:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if record.event_type == "task_execution_planned":
|
if record.event_type == "task_execution_planned":
|
||||||
|
plan_mode = payload.get("plan_mode") or "single"
|
||||||
strategy = payload.get("strategy") or "single"
|
strategy = payload.get("strategy") or "single"
|
||||||
node_ids = payload.get("node_ids") or []
|
node_ids = payload.get("node_ids") or []
|
||||||
root["title"] = f"{payload.get('plan_mode', 'single')} plan: {strategy}"
|
root["title"] = f"{plan_mode} plan: {strategy}"
|
||||||
root["summary"] = payload.get("reason") or ""
|
root["summary"] = payload.get("reason") or ""
|
||||||
root["metadata"] = {
|
root["metadata"] = {
|
||||||
**root.get("metadata", {}),
|
**root.get("metadata", {}),
|
||||||
@ -92,36 +93,65 @@ class SessionProcessProjector:
|
|||||||
add_event(
|
add_event(
|
||||||
event_id=_event_id(record, "planned"),
|
event_id=_event_id(record, "planned"),
|
||||||
run_id=root_run_id,
|
run_id=root_run_id,
|
||||||
kind="run_started",
|
kind="task_planned",
|
||||||
actor_type="system",
|
actor_type="system",
|
||||||
actor_id="task",
|
actor_id="task",
|
||||||
actor_name="Task Planner",
|
actor_name="Task Planner",
|
||||||
text=f"Planned {payload.get('plan_mode')} execution via {strategy}. {payload.get('reason') or ''}".strip(),
|
text=f"Beaver planned {plan_mode} execution via {strategy}. {payload.get('reason') or ''}".strip(),
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
status="running",
|
status="running",
|
||||||
metadata=root["metadata"],
|
metadata={
|
||||||
|
**root["metadata"],
|
||||||
|
"timeline_type": "plan",
|
||||||
|
"user_summary": f"Beaver will use {plan_mode} execution for this task.",
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
selected_skill_names = [
|
||||||
|
str(item)
|
||||||
|
for item in payload.get("selected_skill_names") or []
|
||||||
|
if str(item).strip()
|
||||||
|
]
|
||||||
|
if selected_skill_names:
|
||||||
|
add_event(
|
||||||
|
event_id=_event_id(record, "skills"),
|
||||||
|
run_id=root_run_id,
|
||||||
|
kind="skill_selected",
|
||||||
|
actor_type="system",
|
||||||
|
actor_id="skill-selector",
|
||||||
|
actor_name="Skill Selector",
|
||||||
|
text=f"Selected skill guidance: {', '.join(selected_skill_names)}.",
|
||||||
|
created_at=created_at,
|
||||||
|
status="done",
|
||||||
|
metadata={
|
||||||
|
"task_id": task_id,
|
||||||
|
"attempt_index": attempt_index,
|
||||||
|
"timeline_type": "skill",
|
||||||
|
"skill_names": selected_skill_names,
|
||||||
|
"reason": payload.get("reason") or "Selected from task planning context.",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
elif record.event_type in {"task_team_run_completed", "task_team_run_failed"}:
|
elif record.event_type in {"task_team_run_completed", "task_team_run_failed"}:
|
||||||
team_success = bool(payload.get("team_success"))
|
team_success = bool(payload.get("team_success"))
|
||||||
root["status"] = "running"
|
root["status"] = "running"
|
||||||
|
team_run_ids = payload.get("team_run_ids") or []
|
||||||
root["metadata"] = {
|
root["metadata"] = {
|
||||||
**root.get("metadata", {}),
|
**root.get("metadata", {}),
|
||||||
"team_success": team_success,
|
"team_success": team_success,
|
||||||
"team_run_ids": payload.get("team_run_ids") or [],
|
"team_run_ids": team_run_ids,
|
||||||
"team_error": payload.get("error"),
|
"team_error": payload.get("error"),
|
||||||
}
|
}
|
||||||
add_event(
|
add_event(
|
||||||
event_id=_event_id(record, "team"),
|
event_id=_event_id(record, "team"),
|
||||||
run_id=root_run_id,
|
run_id=root_run_id,
|
||||||
kind="run_status",
|
kind="agent_team_created",
|
||||||
actor_type="system",
|
actor_type="system",
|
||||||
actor_id="team",
|
actor_id="team",
|
||||||
actor_name="Task Team",
|
actor_name="Task Team",
|
||||||
text=payload.get("error") or ("Team completed" if team_success else "Team completed with failed nodes"),
|
text=payload.get("error") or ("Team completed" if team_success else "Team completed with failed nodes"),
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
status="done" if team_success else "error",
|
status="done" if team_success else "error",
|
||||||
metadata=dict(payload),
|
metadata={**dict(payload), "timeline_type": "agent_team", "team_run_ids": team_run_ids},
|
||||||
)
|
)
|
||||||
node_results = payload.get("node_results") or []
|
node_results = payload.get("node_results") or []
|
||||||
for item in node_results:
|
for item in node_results:
|
||||||
@ -192,14 +222,20 @@ class SessionProcessProjector:
|
|||||||
event_id=f"{_event_id(record, 'node')}:{item.get('node_id')}",
|
event_id=f"{_event_id(record, 'node')}:{item.get('node_id')}",
|
||||||
run_id=str(node_run_id),
|
run_id=str(node_run_id),
|
||||||
parent_run_id=root_run_id,
|
parent_run_id=root_run_id,
|
||||||
kind="run_finished",
|
kind="agent_finished",
|
||||||
actor_type="agent",
|
actor_type="agent",
|
||||||
actor_id=str(item.get("node_id") or "sub-agent"),
|
actor_id=str(item.get("node_id") or "sub-agent"),
|
||||||
actor_name=str(item.get("node_id") or "Sub-agent"),
|
actor_name=str(item.get("node_id") or "Sub-agent"),
|
||||||
text=_truncate(str(item.get("output_text") or item.get("error") or "")),
|
text=_truncate(str(item.get("output_text") or item.get("error") or "")),
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
status=status,
|
status=status,
|
||||||
metadata=dict(item),
|
metadata={
|
||||||
|
**dict(item),
|
||||||
|
"task_id": task_id,
|
||||||
|
"attempt_index": attempt_index,
|
||||||
|
"timeline_type": "agent_progress",
|
||||||
|
"node_result": dict(item),
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
elif record.event_type == "task_synthesis_completed":
|
elif record.event_type == "task_synthesis_completed":
|
||||||
@ -242,14 +278,14 @@ class SessionProcessProjector:
|
|||||||
event_id=_event_id(record, "evidence"),
|
event_id=_event_id(record, "evidence"),
|
||||||
run_id=record.run_id or root_run_id,
|
run_id=record.run_id or root_run_id,
|
||||||
parent_run_id=root_run_id if record.run_id else None,
|
parent_run_id=root_run_id if record.run_id else None,
|
||||||
kind="run_status",
|
kind="task_result_ready",
|
||||||
actor_type="system",
|
actor_type="system",
|
||||||
actor_id="evidence-recorder",
|
actor_id="evidence-recorder",
|
||||||
actor_name="Evidence",
|
actor_name="Evidence",
|
||||||
text="Task evidence was recorded; waiting for user acceptance.",
|
text="The task result is ready for user acceptance.",
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
status="done",
|
status="done",
|
||||||
metadata=dict(payload),
|
metadata={**dict(payload), "timeline_type": "result"},
|
||||||
)
|
)
|
||||||
|
|
||||||
elif record.event_type == "task_acceptance_recorded":
|
elif record.event_type == "task_acceptance_recorded":
|
||||||
@ -267,14 +303,14 @@ class SessionProcessProjector:
|
|||||||
event_id=_event_id(record, "acceptance"),
|
event_id=_event_id(record, "acceptance"),
|
||||||
run_id=record.run_id or root_run_id,
|
run_id=record.run_id or root_run_id,
|
||||||
parent_run_id=root_run_id if record.run_id else None,
|
parent_run_id=root_run_id if record.run_id else None,
|
||||||
kind="run_status",
|
kind="task_acceptance_recorded",
|
||||||
actor_type="user",
|
actor_type="user",
|
||||||
actor_id="user-acceptance",
|
actor_id="user-acceptance",
|
||||||
actor_name="User Acceptance",
|
actor_name="User Acceptance",
|
||||||
text=f"User acceptance recorded: {acceptance_type or 'unknown'}.",
|
text=f"User acceptance recorded: {acceptance_type or 'unknown'}.",
|
||||||
created_at=created_at,
|
created_at=created_at,
|
||||||
status="done",
|
status="done",
|
||||||
metadata=dict(payload),
|
metadata={**dict(payload), "timeline_type": "acceptance"},
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -109,6 +109,18 @@ def test_process_projection_maps_task_team_events(tmp_path: Path) -> None:
|
|||||||
},
|
},
|
||||||
context_visible=False,
|
context_visible=False,
|
||||||
)
|
)
|
||||||
|
session.append_message(
|
||||||
|
"web:test",
|
||||||
|
run_id="main-run",
|
||||||
|
role="system",
|
||||||
|
event_type="task_acceptance_recorded",
|
||||||
|
event_payload={
|
||||||
|
"task_id": "task-1",
|
||||||
|
"attempt_index": 1,
|
||||||
|
"acceptance_type": "accept",
|
||||||
|
},
|
||||||
|
context_visible=False,
|
||||||
|
)
|
||||||
|
|
||||||
projection = SessionProcessProjector(session, run_store).project("web:test")
|
projection = SessionProcessProjector(session, run_store).project("web:test")
|
||||||
|
|
||||||
@ -123,6 +135,28 @@ def test_process_projection_maps_task_team_events(tmp_path: Path) -> None:
|
|||||||
assert any(event["actor_name"] == "Evidence" for event in projection["events"])
|
assert any(event["actor_name"] == "Evidence" for event in projection["events"])
|
||||||
assert any(run["session_id"] == "web:test" for run in projection["runs"])
|
assert any(run["session_id"] == "web:test" for run in projection["runs"])
|
||||||
|
|
||||||
|
planned_event = next(event for event in projection["events"] if event["kind"] == "task_planned")
|
||||||
|
assert planned_event["metadata"]["timeline_type"] == "plan"
|
||||||
|
assert planned_event["metadata"]["selected_skill_names"] == ["research-workflow"]
|
||||||
|
|
||||||
|
skill_event = next(event for event in projection["events"] if event["kind"] == "skill_selected")
|
||||||
|
assert skill_event["metadata"]["timeline_type"] == "skill"
|
||||||
|
assert skill_event["metadata"]["skill_names"] == ["research-workflow"]
|
||||||
|
|
||||||
|
team_event = next(event for event in projection["events"] if event["kind"] == "agent_team_created")
|
||||||
|
assert team_event["metadata"]["timeline_type"] == "agent_team"
|
||||||
|
assert team_event["metadata"]["team_run_ids"] == ["sub-run"]
|
||||||
|
|
||||||
|
node_event = next(event for event in projection["events"] if event["kind"] == "agent_finished")
|
||||||
|
assert node_event["metadata"]["timeline_type"] == "agent_progress"
|
||||||
|
|
||||||
|
evidence_event = next(event for event in projection["events"] if event["kind"] == "task_result_ready")
|
||||||
|
assert evidence_event["metadata"]["timeline_type"] == "result"
|
||||||
|
assert evidence_event["status"] == "done"
|
||||||
|
|
||||||
|
acceptance_event = next(event for event in projection["events"] if event["kind"] == "task_acceptance_recorded")
|
||||||
|
assert acceptance_event["metadata"]["timeline_type"] == "acceptance"
|
||||||
|
|
||||||
|
|
||||||
def test_process_projection_exposes_ephemeral_guidance_artifacts(tmp_path: Path) -> None:
|
def test_process_projection_exposes_ephemeral_guidance_artifacts(tmp_path: Path) -> None:
|
||||||
session = SessionManager(tmp_path)
|
session = SessionManager(tmp_path)
|
||||||
|
|||||||
Reference in New Issue
Block a user