diff --git a/.gitea/workflows/agent.yml b/.gitea/workflows/agent.yml index c5d423e..daf278e 100644 --- a/.gitea/workflows/agent.yml +++ b/.gitea/workflows/agent.yml @@ -174,29 +174,6 @@ jobs: "### comment by \($who):\n\(.body)\n"' > /tmp/thread.md 2>/dev/null || true echo "thread comments fetched: $(grep -c '^### comment by ' /tmp/thread.md 2>/dev/null || echo 0)" - # TEMPORARY: capture opencode's --format json event schema and POST it as an issue comment - # (easier to find than reusable-workflow step logs). Removed once captured. - - name: DEBUG capture --format json schema - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GT: ${{ secrets.GITEA_TOKEN }} - NUM: ${{ github.event.issue.number }} - run: | - cd /tmp && rm -f probe.txt - opencode run --model anthropic/claude-sonnet-5 --auto --format json \ - "Create a file /tmp/probe.txt containing the word hi, then say one sentence about what you did." \ - > /tmp/events.jsonl 2>/tmp/events.err || true - n=$(wc -l < /tmp/events.jsonl) - types=$(jq -rc '.type // (keys|join("+"))' /tmp/events.jsonl 2>/dev/null | sort | uniq -c) - examples=$(jq -s -c 'map(select(type=="object" and .type!=null)) | group_by(.type) | map(.[0])[]' /tmp/events.jsonl 2>/dev/null | cut -c1-700) - [ -z "$examples" ] && examples=$(head -c 3000 /tmp/events.jsonl) - body=$(printf 'DEBUG opencode --format json — %s events\n\n**types:**\n```\n%s\n```\n\n**one example per type:**\n```\n%s\n```\n\n**stderr tail:**\n```\n%s\n```' \ - "$n" "$types" "$examples" "$(tail -8 /tmp/events.err)") - API="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" - curl -sS -X POST -H "Authorization: token $GT" -H "Content-Type: application/json" \ - "$API/issues/$NUM/comments" -d "$(jq -nc --arg b "$body" '{body:$b}')" \ - -w '\ndebug comment -> HTTP %{http_code}\n' || true - - name: Run agent id: run env: @@ -288,22 +265,55 @@ jobs: LATEST INSTRUCTION FROM MAINTAINER: ${CMT}" echo "opencode version: $(opencode --version 2>&1)" - # Success is exit code 0 — the agent may make tool-only changes with no text summary, - # so DO NOT treat empty stdout as failure. Only retry a non-zero exit that looks transient. + # Capture the raw JSON event stream (--format json) so the activity log can be built + # from it afterwards. The plain --auto reply text == concatenation of all assistant + # "text" parts, so reconstruct /tmp/agent_out.md from those — the Publish step below + # keeps reading agent_out.md exactly as before. Success is exit code 0: the agent may + # make tool-only changes with no text summary, so DO NOT treat empty output as failure. rc=1 for attempt in 1 2 3; do echo "opencode attempt $attempt/3 for @$NAME ($MODEL)" rc=0 - opencode run --model "$MODEL" --auto "$PROMPT" ${{ steps.imgs.outputs.files }} \ - >/tmp/agent_out.md 2>/tmp/agent_err.log || rc=$? - echo "rc=$rc"; echo "--- stdout (reply) ---"; cat /tmp/agent_out.md; echo "--- stderr (trace) ---"; cat /tmp/agent_err.log + opencode run --model "$MODEL" --auto --format json "$PROMPT" ${{ steps.imgs.outputs.files }} \ + >/tmp/events.jsonl 2>/tmp/agent_err.log || rc=$? + echo "rc=$rc"; echo "--- events ($(wc -l < /tmp/events.jsonl 2>/dev/null || echo 0) lines) ---" + echo "--- stderr (trace) ---"; cat /tmp/agent_err.log [ $rc -eq 0 ] && break - if grep -qiE 'overloaded|429|529|rate.?limit|timeout|ETIMEDOUT|ECONNRESET|EAI_AGAIN' /tmp/agent_out.md /tmp/agent_err.log; then + if grep -qiE 'overloaded|429|529|rate.?limit|timeout|ETIMEDOUT|ECONNRESET|EAI_AGAIN' /tmp/events.jsonl /tmp/agent_err.log; then echo "transient error — backing off $((attempt*20))s"; sleep $((attempt * 20)); continue fi echo "non-transient failure (rc=$rc) — not retrying"; break done [ $rc -eq 0 ] || { echo "agent failed"; exit 1; } + # Reconstruct the plain-text reply from assistant text parts (== what plain --auto prints). + jq -r 'select(.type=="text") | .part.text // ""' /tmp/events.jsonl > /tmp/agent_out.md 2>/dev/null || true + echo "reconstructed reply ($(wc -l < /tmp/agent_out.md 2>/dev/null || echo 0) lines):"; cat /tmp/agent_out.md + + - name: Build activity log (tool calls + reasoning) from the event stream + id: log + env: + MODE: ${{ steps.prep.outputs.mode }} + run: | + # Only dev agents (mode=pr) get an activity-log comment — comment-only roles (pm/qa) + # do no tool calls, so a trail would be empty/noise. + if [ "$MODE" != "pr" ]; then + echo "skipping activity log for comment-mode agent"; : > /tmp/activity_log.md; exit 0 + fi + jq -r ' + def trunc(n): if length > n then (.[0:n] + "…") else . end; + select(.type=="tool_use" or .type=="text") | + if .type=="text" then + "💬 " + ((.part.text // "") | trunc(4000)) + else + (.part.tool // "?") as $t | + ((.part.state.title // (.part.state.input | tojson | trunc(160)) // "")) as $title | + "🔧 **" + $t + "**: `" + ($title | trunc(240)) + "`" + end + ' /tmp/events.jsonl > /tmp/activity_log.md 2>/dev/null || true + n=$(wc -l < /tmp/activity_log.md 2>/dev/null || echo 0) + echo "activity log: $n entries" + [ "$n" -eq 0 ] && : > /tmp/activity_log.md + head -3 /tmp/activity_log.md - name: Publish — PR (dev agents) or comment (pm), always reply in the issue env: @@ -464,6 +474,15 @@ jobs: post "$(printf '🤖 **@%s** — updated %s branch/PR:%b\n\n%s' "$NAME" "$count" "$links" "$prdesc")" fi + # Post the agent's activity trail (tool calls + reasoning) as a separate comment so + # it is visible on the PR thread. Additive — kept here even when nothing changed, so a + # follow-up run (re-trigger) can see what this run did via the fetched issue thread. + if [ -s /tmp/activity_log.md ]; then + entries=$(wc -l < /tmp/activity_log.md 2>/dev/null || echo 0) + log=$(cat /tmp/activity_log.md) + post "$(printf '🤖 **@%s** — activity log (%s entries):\n
\ntool calls & reasoning\n\n%s\n\n
' "$NAME" "$entries" "$log")" + fi + - name: Mark done with 🚀 (remove 👀) env: GT: ${{ secrets.GITEA_TOKEN }}