Skip to content

[fix] Bound the session-records fetch and consume the persist-drop signal - #5501

Open
ardaerzin wants to merge 2 commits into
feat/sessions-last-message-onlyfrom
fix/sessions-records-robustness
Open

[fix] Bound the session-records fetch and consume the persist-drop signal#5501
ardaerzin wants to merge 2 commits into
feat/sessions-last-message-onlyfrom
fix/sessions-records-robustness

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

Context

Two small robustness gaps in the last-message-only reconstruction pipeline (#5486), separate from the QA-finding PRs (#5488-#5491, #5494).

Changes

  • Records fetch had no timeout. fetchSessionRecords sits on the turn's critical path (the prompt waits on it), so a stalled query would hang on Undici's long request-level timeout instead of falling back to the inbound history. Added an env-tunable AbortSignal.timeout (AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS, 5s default).
  • The persist-drop signal was never consumed. takePersistFailures had no caller: in durable mode a dropped record was counted into a per-session map that nothing read, so the signal was lost and the map grew unbounded. Now consumed at the turn-end drain (flush) — it warns when the durable log is incomplete and clears the counter.

Tests

  • Runner 1264 unit tests + typecheck green. Added a flush-consumer test (warns + clears) and switched the existing durable-count test to drain directly, since flush now consumes the count.

Note

Neither is a QA finding; both surfaced while reviewing the reconstruction path. Independent of the other stacked PRs — merges into #5486 in any order.

Two small robustness gaps in the reconstruction pipeline:

- fetchSessionRecords sits on the turn's critical path (the prompt waits on it), but had no
  timeout, so a stalled query would wait on Undici's long request-level timeout instead of
  failing fast to the inbound-history fallback. Add an env-tunable AbortSignal.timeout (5s
  default).
- takePersistFailures had no caller: in durable mode a dropped record was counted into a
  per-session map that nothing read, so the signal was lost and the map grew unbounded.
  Consume it at the turn-end drain (flush) — warn when the durable log is incomplete and
  clear the counter.
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jul 25, 2026
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agenta-documentation Ready Ready Preview, Comment Jul 27, 2026 7:26pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 25, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 03c51288-8194-4cf4-a552-0c017a0eb929

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sessions-records-robustness

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-production-0c92.up.railway.app/w
Image tag pr-5501-aa152b0
Status Failed
Railway logs Open logs
Logs View workflow run
Updated at 2026-07-27T19:47:44.514Z

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 452202da-8419-4099-beb1-53772089ca5d

📥 Commits

Reviewing files that changed from the base of the PR and between 882eebc and 35fbe66.

📒 Files selected for processing (3)
  • services/runner/src/sessions/persist.ts
  • services/runner/src/sessions/records-query.ts
  • services/runner/tests/unit/session-persist.test.ts

Comment on lines +17 to +20
function queryTimeoutMs(): number {
const n = Number(process.env.AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS);
return Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate target file:"
fd -a 'records-query\.ts$' . || true

echo
echo "Relevant source:"
file="$(fd 'records-query\.ts$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  nl -ba "$file" | sed -n '1,120p'
fi

echo
echo "Search usages of queryTimeoutMs:"
rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS" . || true

echo
echo "Runtime/version facts if Node is available:"
if command -v node >/dev/null 2>&1; then
  node -e "console.log(process.version)"
  node - <<'JS'
const tests = [0.001, 0.5, 0.999, 1, 1.999, 2, 5000, -0.001, 0, 1e-9, 1e-5];
console.log("AbortSignal.timeout(0):", AbortSignal.timeout(0).reason);
for (const n of tests) {
  const computed = Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000;
  let reason = "<no signal>";
  if (computed > 0 && computed !== Infinity && Number.isFinite(computed)) {
    try {
      const sig = AbortSignal.timeout(computed);
      reason = sig.reason?.name ?? String(sig.reason);
      sig.addEventListener("abort", () => {
        // no-op
      });
    } catch (e) {
      reason = `${e.name}: ${e.message}`;
    }
  }
  console.log(`${n} -> computed=${computed} signal reason=${reason}`);
}
JS
fi

Repository: Agenta-AI/agenta

Length of output: 298


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd 'records-query\\.ts$' . | head -n 1 || true)"
echo "target=$file"

echo
echo "Relevant source:"
awk '{printf "%6d  %s\n", NR, $0}' "$file" | sed -n '1,120p'

echo
echo "Query timeout usages:"
rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS|AbortSignal\\.timeout" . || true

echo
echo "Runtime/version facts if Node is available:"
if command -v node >/dev/null 2>&1; then
  node -e "console.log(process.version)"
  node - <<'JS'
const tests = [0.001, 0.5, 0.999, 1, 1.999, 2, 5000, -0.001, 0, 1e-9, 1e-5];
console.log("AbortSignal.timeout(0):", AbortSignal.timeout(0).reason);
for (const n of tests) {
  const computed = Number.isFinite(n) && n > 0 ? Math.floor(n) : 5000;
  let reason = "<no signal>";
  if (computed > 0 && computed !== Infinity && Number.isFinite(computed)) {
    try {
      const sig = AbortSignal.timeout(computed);
      sig.addEventListener("abort", () => {});
      reason = sig.reason?.name ?? String(sig.reason);
      console.log(`${n} -> computed=${computed} signal reason=${reason}`);
    } catch (e) {
      console.log(`${n} -> computed=${computed} signal throws ${e.name}: ${e.message}`);
    }
  }
}
JS
fi

Repository: Agenta-AI/agenta

Length of output: 1401


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate records-query.ts:"
fd 'records-query\\.ts$' . || true

echo
echo "target candidate:"
file="$(fd 'records-query\\.ts$' . | head -n 1 || true)"
echo "$file"

if [ -n "$file" ] && [ -f "$file" ]; then
  echo
  echo "Relevant source:"
  awk '{printf "%6d  %s\n", NR, $0}' "$file" | sed -n '1,120p'

  echo
  echo "Query timeout usages:"
  rg -n "queryTimeoutMs|AGENTA_SESSIONS_RECORDS_QUERY_TIMEOUT_MS|AbortSignal\\.timeout" "$file" . || true
fi

Repository: Agenta-AI/agenta

Length of output: 199


Keep the computed timeout strictly positive.

Values below 1ms pass validation, but Math.floor(n) produces 0, so an AbortSignal.timeout(0) signal can abort the query immediately. Clamp or reject sub-millisecond values before constructing the signal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants