Skip to content

symfonic.capabilities.memory.episodic_policy

episodic_policy

Project safe tool provenance from the current Agent transcript.

The previous model rounds are already closed in the typed transcript when the final response is written. Read only messages after this request's last user message, so replayed history cannot be attached to a new episodic exchange.

completed_tool_calls

completed_tool_calls(transcript: Any) -> dict[str, Any]

Return assistant-only categorical tool outcomes.

Raw names, arguments, results, errors and call IDs may contain erased or rejected data. Keep them out of durable episodic metadata by default.

Source code in src/symfonic/capabilities/memory/episodic_policy.py
def completed_tool_calls(transcript: Any) -> dict[str, Any]:
    """Return assistant-only categorical tool outcomes.

    Raw names, arguments, results, errors and call IDs may contain erased or
    rejected data. Keep them out of durable episodic metadata by default.
    """
    messages = getattr(transcript, "typed", ()) or ()
    boundary = next(
        (index for index in range(len(messages) - 1, -1, -1)
         if getattr(messages[index], "role", None) == "user"),
        len(messages),
    )
    calls: list[dict[str, Any]] = []
    for message in messages[boundary + 1:]:
        if getattr(message, "role", None) != "assistant":
            continue
        for call in getattr(message, "tool_calls", ()) or ():
            if call.result is None and call.error is None:
                continue
            calls.append({
                "outcome": "failed" if call.error is not None else "completed",
            })
    return {"tool_calls": calls} if calls else {}