feat(agent-queue): report run insights to the fleet + normalize API base

#1 fleet_report_insights: on a successful fleet run the factory now reports the
parsed cost/token/effort metrics (model, tokensIn/Out/cached, costUsd, turns,
toolCalls) plus the run result onto the coordinator run via POST
.../lease/release (which also frees the lease). parse_usage already extracted
these into the job meta; they were never sent. Engines that do not expose usage
locally (devin) still land result + endedAt.

#2 normalize AQ_FLEET_API: platform-service mounts fleet under /api, so a base
without it silently returned 404 on every call. Strip a trailing slash and
append /api unless already present, so AQ_FLEET_API=http://host:4003 works too.

selftest: +2 cases (insights reported via lease/release; API-base normalization).
Full self-test PASS.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
saravanakumardb1 2026-05-31 02:27:17 -07:00
parent d04c8021ad
commit 57831e3e7a
3 changed files with 69 additions and 0 deletions

View File

@ -816,6 +816,9 @@ run_worker() {
fleet_quarantine "$job" "$doing_file" "$metaf" "$logf"
return 0
fi
# Report cost/token/effort metrics (parse_usage wrote them to the meta just
# above) + result onto the coordinator's run, and release the held lease.
fleet_report_insights "$job" review
fi
# Agent succeeded: land in review/, then run the auto-QA verify gate. The
# worker is still alive here so the concurrency slot stays held through

View File

@ -21,6 +21,11 @@
# ── Config (env-overridable) ────────────────────────────────────────
AQ_FLEET="${AQ_FLEET:-0}" # master switch (0 = offline)
AQ_FLEET_API="${AQ_FLEET_API:-http://localhost:4003/api}" # base URL incl. /api
# Normalize: platform-service mounts the fleet routes under /api. Strip a trailing
# slash and append /api unless already present, so AQ_FLEET_API=http://host:4003
# (the natural form) works too instead of silently 404ing every fleet call.
AQ_FLEET_API="${AQ_FLEET_API%/}"
[[ "$AQ_FLEET_API" == */api ]] || AQ_FLEET_API="${AQ_FLEET_API}/api"
AQ_FLEET_TOKEN="${AQ_FLEET_TOKEN:-}" # bearer; never hardcode
# AQ_PRODUCT_ID is shared with the Slice-4 tracker config (X-Product-Id header).
AQ_FACTORY_ID="${AQ_FACTORY_ID:-$( (hostname -s 2>/dev/null || hostname 2>/dev/null || echo factory) | tr -cd 'A-Za-z0-9._-')-$$}"
@ -259,6 +264,39 @@ fleet_lease_release() {
return 0
}
# fleet_report_insights <job> [result] — report the run's cost/token/effort metrics
# (parsed by parse_usage into the job meta) to the coordinator, recorded on the
# current run. Also releases the held lease (the agent has finished its work unit).
# Best-effort: never blocks the loop. Engines that don't expose usage locally
# (e.g. devin) simply omit token/cost fields; `result` + endedAt still land.
fleet_report_insights() {
fleet_enabled || return 0
local job=$1 result=${2:-} metaf jid epoch
metaf="$STATE/$job.meta"
jid=$(_meta_val "$metaf" fleet_job_id); epoch=$(_meta_val "$metaf" fleet_lease_epoch)
[[ -n "$jid" ]] || return 0
local model ti to tc cost turns tools est ins=""
model=$(_meta_val "$metaf" model)
ti=$(_meta_val "$metaf" tokens_in); to=$(_meta_val "$metaf" tokens_out)
tc=$(_meta_val "$metaf" tokens_cached); cost=$(_meta_val "$metaf" cost_usd)
turns=$(_meta_val "$metaf" turns); tools=$(_meta_val "$metaf" tool_calls)
est=$(_meta_val "$metaf" usage_estimated)
[[ -n "$model" ]] && ins+=",\"model\":\"$(_json_escape "$model")\""
[[ "$ti" =~ ^[0-9]+$ ]] && ins+=",\"tokensIn\":$ti"
[[ "$to" =~ ^[0-9]+$ ]] && ins+=",\"tokensOut\":$to"
[[ "$tc" =~ ^[0-9]+$ ]] && ins+=",\"tokensCached\":$tc"
[[ "$cost" =~ ^[0-9]+(\.[0-9]+)?$ ]] && ins+=",\"costUsd\":$cost"
[[ "$turns" =~ ^[0-9]+$ ]] && ins+=",\"turns\":$turns"
[[ "$tools" =~ ^[0-9]+$ ]] && ins+=",\"toolCalls\":$tools"
[[ "$est" == "true" || "$est" == "1" ]] && ins+=",\"estimated\":true"
local body="{\"leaseEpoch\":${epoch:-0}"
[[ -n "$ins" ]] && body+=",\"insights\":{${ins#,}}"
[[ -n "$result" ]] && body+=",\"result\":\"$(_json_escape "$result")\""
body+="}"
_fleet_call POST "/fleet/jobs/$jid/lease/release" "$body"
return 0
}
# fleet_renew_active — renew leases for all in-flight (building/) fleet jobs.
fleet_renew_active() {
fleet_enabled || return 0

View File

@ -1027,6 +1027,34 @@ else
fi
unset AQ_FLEET_API_CMD AQ_FLEET_SHADOW_LOG AGENT_QUEUE_ROOT
# fleet insights (§26): a successful fleet run reports parsed cost/token metrics +
# result onto the coordinator's run via POST .../lease/release (fleet_report_insights).
export AGENT_QUEUE_ROOT="$tmp/queue-fl-ins"; export AQ_FLEET_CWD="$work"
"$AQ" init >/dev/null
export AQ_FLEET_API_CMD="$fstub" AQ_FSTUB_CALLS="$tmp/fl-ins-calls.log" \
AQ_FSTUB_CLAIM_FLAG="$tmp/fl-ins-claimed" AQ_FSTUB_JOB_ID="fjob_ins" AQ_FSTUB_BODY="emit usage"
: > "$AQ_FSTUB_CALLS"; rm -f "$AQ_FSTUB_CLAIM_FLAG"
# usagestub emits an AQ_USAGE line; materialized fleet jobs run the default (devin) engine.
AQ_FLEET=1 AGENT_QUEUE_POLL=1 DEVIN_BIN="$usagestub" "$AQ" run --once >/dev/null 2>&1
if grep -qE 'POST /fleet/jobs/fjob_ins/lease/release :: .*"tokensIn":100' "$AQ_FSTUB_CALLS" \
&& grep -qE 'POST /fleet/jobs/fjob_ins/lease/release :: .*"costUsd":0\.0021' "$AQ_FSTUB_CALLS" \
&& grep -qE 'POST /fleet/jobs/fjob_ins/lease/release :: .*"result":"review"' "$AQ_FSTUB_CALLS"; then
pass "fleet insights: run reports cost/tokens + result via lease/release"
else
cat "$AQ_FSTUB_CALLS" >&2; fail "fleet insights not reported on lease/release"
fi
unset AQ_FLEET_API_CMD AQ_FSTUB_CALLS AQ_FSTUB_CLAIM_FLAG AQ_FSTUB_JOB_ID AQ_FSTUB_BODY AGENT_QUEUE_ROOT AQ_FLEET_CWD
# fleet API base normalization: AQ_FLEET_API without the /api mount must be
# normalized so fleet calls still resolve (regression for the silent-404 bug).
norm=$(AQ_FLEET_API="http://localhost:4003" bash -c 'source "'"$HERE"'/lib/fleet-client.sh"; printf "%s" "$AQ_FLEET_API"')
norm2=$(AQ_FLEET_API="http://localhost:4003/api/" bash -c 'source "'"$HERE"'/lib/fleet-client.sh"; printf "%s" "$AQ_FLEET_API"')
if [ "$norm" = "http://localhost:4003/api" ] && [ "$norm2" = "http://localhost:4003/api" ]; then
pass "fleet API base: normalized to include exactly one /api (with or without)"
else
fail "fleet API base normalization wrong (got '$norm' and '$norm2')"
fi
# ─────────────────────────────────────────────────────────────────────
# Phase 2 — two-factory parallel demo (EXIT CRITERIA, §14). Runs the demo
# HEADLESS in STUB mode (its own stateful coordinator stub + two real factory