Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/arbiter/lib/arbiter/mcp/tools/worker.ex
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,8 @@ defmodule Arbiter.MCP.Tools.Worker do
model: run.model,
provider: run.provider,
provider_fallback: run.provider_fallback,
session_id: run.session_id,
resumed_from_run_id: run.resumed_from_run_id,
started_at: Tools.iso(run.started_at),
completed_at: Tools.iso(run.completed_at),
exit_code: run.exit_code,
Expand Down
44 changes: 44 additions & 0 deletions apps/arbiter/test/arbiter/mcp/tools_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3354,6 +3354,50 @@ defmodule Arbiter.MCP.ToolsTest do
assert entry.difficulty_at_dispatch == 3
end

# bd-b7e33c post-merge finding (2026-09-22): only `provider` was surfaced
# here; `session_id` and `resumed_from_run_id` were silently dropped, the
# same gap the REST `/api/workers/history` endpoint had — which is what
# made the 04:26Z production verification misread a captured agy
# conversation id as NULL. Surface both so resume continuity is directly
# observable through this tool too.
test "surfaces session_id and resumed_from_run_id", ctx do
{:ok, task} = Ash.create(Issue, %{title: "session fields target", workspace_id: ctx.ws.id})

{:ok, prior} =
Ash.create(Arbiter.Workers.Run, %{
task_id: task.id,
repo: "arbiter",
workspace_id: ctx.ws.id,
status: :completed,
provider: "gemini",
session_id: "25df47b0-054e-434e-84c1-6876fd9f77de",
started_at: DateTime.add(DateTime.utc_now(), -600, :second)
})

{:ok, resumed} =
Ash.create(Arbiter.Workers.Run, %{
task_id: task.id,
repo: "arbiter",
workspace_id: ctx.ws.id,
status: :completed,
provider: "gemini",
session_id: "89a2b784-6bd5-46e6-a971-2178ca58cdcd",
resumed_from_run_id: prior.id,
started_at: DateTime.utc_now()
})

assert {:ok, %{runs: [resumed_entry, prior_entry]}} =
Tools.worker_runs(ctx.coordinator, %{"task_id" => task.id})

assert prior_entry.id == prior.id
assert prior_entry.session_id == "25df47b0-054e-434e-84c1-6876fd9f77de"
assert prior_entry.resumed_from_run_id == nil

assert resumed_entry.id == resumed.id
assert resumed_entry.session_id == "89a2b784-6bd5-46e6-a971-2178ca58cdcd"
assert resumed_entry.resumed_from_run_id == prior.id
end

test "honors a bounded limit", ctx do
{:ok, task} = Ash.create(Issue, %{title: "many runs", workspace_id: ctx.ws.id})

Expand Down
5 changes: 4 additions & 1 deletion apps/arbiter_web/lib/arbiter_web/controllers/api/run_json.ex
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ defmodule ArbiterWeb.Api.RunJSON do
routing_policy: r.routing_policy,
model_tier: r.model_tier,
thinking: r.thinking,
difficulty_at_dispatch: r.difficulty_at_dispatch
difficulty_at_dispatch: r.difficulty_at_dispatch,
provider: r.provider,
session_id: r.session_id,
resumed_from_run_id: r.resumed_from_run_id
}
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,45 @@ defmodule ArbiterWeb.Api.RunControllerTest do
assert entry["thinking"] == "medium"
assert entry["difficulty_at_dispatch"] == 2
end

# bd-b7e33c post-merge finding (2026-09-22): the 04:26Z production
# verification misread an agy run as having `session_id: NULL` /
# `provider: null` because `arb worker runs --json` (this endpoint) never
# surfaced those columns at all — they were silently dropped from the
# summary, not actually null in `worker_runs`. Surface them so a resume's
# conversation continuity (or lack of it) is directly observable from the
# CLI/API without reaching into the DB.
test "lists provider/session_id/resumed_from_run_id", %{conn: conn} do
now = DateTime.utc_now()

prior =
insert_run!(%{
task_id: "bd-session-fields",
provider: "gemini",
session_id: "25df47b0-054e-434e-84c1-6876fd9f77de",
started_at: DateTime.add(now, -600, :second)
})

_resumed =
insert_run!(%{
task_id: "bd-session-fields",
provider: "gemini",
session_id: "89a2b784-6bd5-46e6-a971-2178ca58cdcd",
resumed_from_run_id: prior.id,
started_at: now
})

conn = get(conn, ~p"/api/workers/history", %{task_id: "bd-session-fields"})
[resumed_entry, prior_entry] = json_response(conn, 200)["data"]

assert prior_entry["provider"] == "gemini"
assert prior_entry["session_id"] == "25df47b0-054e-434e-84c1-6876fd9f77de"
assert prior_entry["resumed_from_run_id"] == nil

assert resumed_entry["provider"] == "gemini"
assert resumed_entry["session_id"] == "89a2b784-6bd5-46e6-a971-2178ca58cdcd"
assert resumed_entry["resumed_from_run_id"] == prior.id
end
end

describe "GET /api/workers/history/:id" do
Expand Down
Loading