Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
68eb2b2
feat(dashboard): add agentic conversation tracking with live transcri…
hartmark Aug 4, 2026
cb4008b
test: update sidebar tests and Vietnamese translations for the new Co…
hartmark Aug 4, 2026
1741302
fix(docs): add required frontmatter to AGENTROUTER_WAF.md
hartmark Aug 4, 2026
dc1d289
fix(sse): fix stale provider-response summaries and broken conversati…
hartmark Aug 4, 2026
c91aa3d
fix(ci): resolve migration collision, file-size gate, and truncated-b…
hartmark Aug 4, 2026
e71cf49
fix(ci): re-baseline vulnCount for pre-existing upstream CVE drift
hartmark Aug 4, 2026
1a34210
fix(conversation): show a placeholder for truncated bodies with no kn…
hartmark Aug 5, 2026
ae2e89d
feat(logging): make the chat-log truncation limit configurable, bumpe…
hartmark Aug 5, 2026
02114a1
feat(conversations): redesign to no-forking model with pagination and…
hartmark Aug 6, 2026
d68838d
refactor(dashboard): simplify request detail panel, fix Responses API…
hartmark Aug 6, 2026
4c3596b
fix(dashboard): Previous/Next nav closing modal on stale background list
hartmark Aug 6, 2026
17ac687
fix(chatcore): restore missing mergeResponseToolNameMap import
hartmark Aug 6, 2026
0dd04e7
feat(dashboard): prev/next conversation nav + live streaming turn pre…
hartmark Aug 6, 2026
7a96311
fix(conversations): stop truncated tool_use previews from breaking JSON
hartmark Aug 6, 2026
67e6828
fix(sse): provider-response summary used the client's format, not the…
hartmark Aug 6, 2026
af9c37a
fix(combo): remove duplicate HARD_COMPAT_REASONS declaration
hartmark Aug 6, 2026
ce502b5
chore(db): fix stale self-referencing numbers in migration header com…
hartmark Aug 6, 2026
e6e463f
fix(dashboard): reassemble split chunk-log entries before parsing liv…
hartmark Aug 7, 2026
709f8e5
fix(build): register onnxruntime-node's native bin/ as a standalone a…
hartmark Aug 7, 2026
7ecaa7a
fix(combo): restore HARD_COMPAT_REASONS declaration dropped by a stal…
hartmark Aug 8, 2026
e5dfe71
chore: resync package-lock.json after rebase onto upstream/release/v3…
hartmark Aug 10, 2026
3a32997
fix(tests): use dynamic imports so DATA_DIR override actually takes e…
hartmark Aug 10, 2026
60ed352
fix(sse): stamp object: chat.completion on the provider-summary fallback
hartmark Aug 10, 2026
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
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,11 @@ APP_LOG_TO_FILE=true
# CHAT_LOG_ARRAY_TAIL_ITEMS=128 # Number of array items retained from tail (default: 128)
# CHAT_LOG_MAX_DEPTH=6 # Max nesting depth before truncation (default: 6)
# CHAT_LOG_MAX_OBJECT_KEYS=80 # Max object keys retained (default: 80, 0 = no limit)
# CHAT_LOG_MAX_BODY_KB=1024 # Whole request/response body size before it's replaced by a bare
# {_truncated, messageCount, ...} summary instead of the full clone
# (default: 1024 KB / 1MB). Raise this if the dashboard's "Full
# Conversation" transcript panel shows a placeholder instead of the
# actual messages for long agentic conversations.

# Maximum rows in the proxy_logs SQLite table.
# Default: 100000
Expand Down
5 changes: 3 additions & 2 deletions config/quality/quality-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@
"_rebaseline_2026_07_28_ci_runner_delta": "189 -> 190 (+1). Medido 189 no devbox e 190 no runner do GitHub no MESMO commit (run 30396592013, job Quality Gates (Extended)) — mesma classe já registrada em _rebaseline_2026_07_20_aliasresolver_hook_split_7808: a versão do zizmor no runner enxerga uma finding a mais que a local, sempre da classe unpinned-uses @vN. O valor do runner é o que o gate compara, então a baseline segue o runner."
},
"vulnCount": {
"value": 10,
"value": 22,
"direction": "down",
"dedicatedGate": true
},
Expand Down Expand Up @@ -391,5 +391,6 @@
"_zizmor_rebaseline_2026_06_19_a11y_148_reconcile": "RECONCILIACAO CROSS-PR (release-volatil) ao mergear #4321 (a11y) APOS #4322 (R1): zizmorFindings 145 -> 148. O #4322 ja rebaselinou 139->145 (drift base 142 + 3 unpinned-uses do mutation-redundancy.yml). Este PR adiciona +3 unpinned-uses @vN do novo job 'a11y' (nightly-resilience.yml): actions/checkout@v7, actions/setup-node@v6, actions/cache@v5.0.5 — MESMA convencao @vN deliberada e INTOCADA de todos os workflows (ver _scanner_harden_workflows_2026_06_16). Total = 142 base + 3 r1 + 3 a11y = 148, MEDIDO com `node scripts/check/check-workflows.mjs --ratchet` na arvore release(com #4322)+#4321 = 148 exato. Nenhum template-injection/artipacked/cache-poisoning novo.",
"_zizmor_rebaseline_2026_06_20_ci_build_artifact_reuse": "zizmorFindings 148 -> 152. Drift legitimo deste PR ao reutilizar o artefato next-build do job Build em package-artifact/electron-package-smoke e ao separar o build de compatibilidade Node 26: +4 unpinned-uses novos (2x actions/download-artifact@v8, actions/checkout@v7, actions/setup-node@v6). Mantida a convencao deliberada @vN dos workflows (sem SHA-pinning/manual update burden), conforme precedentes _scanner_harden_workflows_2026_06_16 e _zizmor_rebaseline_2026_06_19_*. Sem novos findings de template-injection/artipacked/cache-poisoning; medido localmente com zizmor 1.25.2 via `npm run check:workflows -- --ratchet` = 152.",
"_cognitive_rebaseline_2026_07_27_3850_relax_v2_20pct": "cognitiveComplexity 971->1223 (+252, +26.0% over pristine 971). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). v1 was +48 on 2026-07-27; v2 = v1 +20% buffer = +58 → +252 total (cycle 971 measured pristine → 1223 ceiling). Justification: same as complexity v2 — the v3.8.50 release cut coincides with high-merge activity; owner accepted enlarging the headroom to cover the entire PREPARE phase (5 minor cycles .50-.54) without per-PR rebaseline noise, given that re-tightening is mechanical at v3.8.51 via the combo.ts/chatCore.ts decomposition work scheduled in .51/.52 (ROADMAP.md). RE-TIGHTENING MANDATORY in v3.8.51: target 1009 (shrink of 214 from structural extraction during the decomposition campaigns, or via npm run quality:ratchet -- --update if natural shrink appears earlier). The 1009 floor still gives 38 units of post-tighten headroom vs the current pristine 971. Tracked via same roadmap issue as complexity v2. Window: v3.8.50 (release cut) → v3.8.54 close (RE-TIGHTEN at v3.8.51 prep merge per ROADMAP.md). Last entry unless measured regression. v1 entry retained below for audit trail.",
"_cognitive_rebaseline_2026_07_27_3850_relax": "cognitiveComplexity 971->1019 (+48). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). +48 covers Train 1D (+15) + headroom for 3.8.50/.51 batches. RE-TIGHTENING MANDATORY in v3.8.51: target 1009 (from combo.ts/chatCore.ts decomposition scheduled in .51/.52 per ROADMAP.md phases). Tracked via same roadmap issue as complexity. SUPERSEDED by _cognitive_rebaseline_2026_07_27_3850_relax_v2_20pct (v1 +20% buffer) — retained for audit. Last entry unless measured regression."
"_cognitive_rebaseline_2026_07_27_3850_relax": "cognitiveComplexity 971->1019 (+48). OWNER-APPROVED TEMPORARY relax for v3.8.50-3.8.54 PREPARE phase (docs/ROADMAP.md). +48 covers Train 1D (+15) + headroom for 3.8.50/.51 batches. RE-TIGHTENING MANDATORY in v3.8.51: target 1009 (from combo.ts/chatCore.ts decomposition scheduled in .51/.52 per ROADMAP.md phases). Tracked via same roadmap issue as complexity. SUPERSEDED by _cognitive_rebaseline_2026_07_27_3850_relax_v2_20pct (v1 +20% buffer) — retained for audit. Last entry unless measured regression.",
"_vuln_rebaseline_2026_08_04_9439_cve_drift": "vulnCount 10->22 (HIGH=10, MODERATE=12, measured by osv-scanner v2.3.8 in PR #9439's own CI run). This is CVE variance, not a dependency change made by this PR: `git diff upstream/release/v3.8.50 HEAD -- package.json package-lock.json` is empty — neither file was touched anywhere in this branch's history. The osv-scanner vulnerability ratchet apparently does not run on every commit landed directly to release/v3.8.50 (same 'fast-gate PR->release skips this check' pattern already documented for check:file-size, e.g. _rebaseline_2026_07_01_v3843_release_5609), so newly-disclosed CVEs in already-present transitive dependencies accumulated on the release branch and only surfaced here because this PR's rebase onto the current release/v3.8.50 tip pulled them in. This exact scenario — 'a newly-disclosed CVE in an already-present dep can trip the gate with no dependency change on your part' — is the documented expected behavior in _osv_flip_blocking_2026_06_16_v3827 above, whose prescribed remedy is 'bump the dep, or re-baseline vulnCount with justification+issue' (docs/security/SUPPLY_CHAIN.md -> 'Variância de CVE'). osv-scanner is not available in this sandbox to enumerate the exact GHSA/CVE ids and safely bump only the affected transitive deps without a broader, separately-scoped dependency-audit pass; re-baselining here unblocks this PR without masking anything introduced by it. Tracked for follow-up: a dedicated dependency-bump PR should re-tighten vulnCount back down once the specific advisories are enumerated locally with osv-scanner installed."
}
1 change: 1 addition & 0 deletions docs/reference/ENVIRONMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,7 @@ The logging system writes to both stdout and rotated log files. All configuratio
| `CHAT_LOG_ARRAY_TAIL_ITEMS` | `128` | Number of array items retained from the tail when truncating chat log payloads. |
| `CHAT_LOG_MAX_DEPTH` | `6` | Max nesting depth before chat log payloads are truncated. |
| `CHAT_LOG_MAX_OBJECT_KEYS` | `80` | Max object keys retained in chat log payloads (0 = unlimited). |
| `CHAT_LOG_MAX_BODY_KB` | `1024` | Whole request/response body size (KB) before it's replaced by a bare summary instead of the full clone. Raise this if long agentic conversations show a placeholder instead of the real messages in the dashboard. |
| `CHAT_DEBUG_FILE` | `false` | When true, `serializeArtifactForStorage` skips size-based truncation. Debug only. |

---
Expand Down
32 changes: 16 additions & 16 deletions docs/security/AGENTROUTER_WAF.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "AgentRouter WAF"
title: "agentrouter.org WAF"
version: 3.8.50
lastUpdated: 2026-08-03
lastUpdated: 2026-08-04
---

# agentrouter.org WAF (Web Application Firewall)
Expand Down Expand Up @@ -29,22 +29,22 @@ The WAF inspects `messages[].content` only. It does **not** inspect:

## Always-blocked patterns (case-insensitive)

| Pattern | Notes |
|-------------------------------|----------------------------------------|
| Any `Lorem ipsum` variant | Full Latin lorem vocabulary is blocked |
| `language model` (alone) | "the language model" and "large language model" pass |
| `virtual assistant` | "AI assistant" passes |
| `I'm here to help` | "here to help" alone also blocks |
| `Claude, made by Anthropic` | Full phrase only |
| Pattern | Notes |
| --------------------------- | ---------------------------------------------------- |
| Any `Lorem ipsum` variant | Full Latin lorem vocabulary is blocked |
| `language model` (alone) | "the language model" and "large language model" pass |
| `virtual assistant` | "AI assistant" passes |
| `I'm here to help` | "here to help" alone also blocks |
| `Claude, made by Anthropic` | Full phrase only |

## Almost-always-blocked patterns

| Pattern | Notes |
|-------------------|---------------------------------------------------------|
| `placeholder` | When it stands alone (not as a parameter name, etc.) |
| `dummy data` | Common seed phrase for fixtures |
| `foo bar baz` | Canonical placeholder phrase |
| Repeated short tokens (`AAA BBB CCC`, `test test test`) | Detector for keyword stuffing |
| Pattern | Notes |
| ------------------------------------------------------- | ---------------------------------------------------- |
| `placeholder` | When it stands alone (not as a parameter name, etc.) |
| `dummy data` | Common seed phrase for fixtures |
| `foo bar baz` | Canonical placeholder phrase |
| Repeated short tokens (`AAA BBB CCC`, `test test test`) | Detector for keyword stuffing |

## Behavior under load

Expand Down Expand Up @@ -94,4 +94,4 @@ The current filter is overly aggressive — it blocks "Lorem ipsum" in
`tool_result` blocks even though the operator clearly did not intend to
inject a prompt. Operators who want this fixed at the source should
contact `agentrouter.org` to report the false positives. The blocklist
above is the empirical result of probing the upstream as of 2026-08-03.
above is the empirical result of probing the upstream as of 2026-08-03.
8 changes: 7 additions & 1 deletion open-sse/handlers/chatCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ export async function handleChatCore({
skipUpstreamRetry = false,
createPiiTransform = null,
correlationId = null,
conversationId = null,
modelPinned = false,
skipResourcePressureGuard = false,
}) {
Expand Down Expand Up @@ -771,6 +772,7 @@ export async function handleChatCore({
providerRequest: initialProviderRequest,
stage: "registered",
correlationId,
sessionTag: conversationId || null,
}) || generateRequestId();

// Initialize rate limit settings from persisted DB (once, lazy)
Expand Down Expand Up @@ -896,7 +898,11 @@ export async function handleChatCore({
noLogEnabled,
correlationId,
modelPinned,
sessionTag: explicitSessionIdHeader,
// Resolved conversationId (open-sse/services/conversationTracker.ts) wins when
// present — it's populated for every request now, not just ones where the
// client explicitly sent x-omniroute-session-id. The raw header remains a
// fallback for any caller that somehow bypassed conversationId resolution.
sessionTag: conversationId || explicitSessionIdHeader,
});

// Primary path: merge client model id + alias target so config on either key applies; resolved
Expand Down
13 changes: 10 additions & 3 deletions open-sse/handlers/chatCore/logTruncation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ export function cloneBoundedChatLogPayload(value: unknown, depth = 0): unknown {

/**
* Truncate a large object for logging. If its JSON representation exceeds
* the configured max body size (getChatLogMaxBodyBytes()), return a
* lightweight summary instead of the full clone. This prevents
* persistAttemptLogs from holding multi-MB references to translatedBody
* getChatLogMaxBodyBytes() (default 1MB; CHAT_LOG_MAX_BODY_KB env override),
* return a lightweight summary instead of the full clone. This prevents
* persistAttemptLogs from holding unbounded references to translatedBody
* across 17 call sites per request.
*
* When the summarized object carries a `tools` definition, re-attach it
Expand All @@ -77,6 +77,9 @@ export function truncateForLog(value: unknown): Record<string, unknown> | null |
if (value === null || value === undefined) return value as null | undefined;
if (typeof value !== "object") return value as unknown as Record<string, unknown>;
const maxBodyBytes = getChatLogMaxBodyBytes();
// Pass maxBodyBytes as the early-exit point — otherwise estimateSizeFast's
// own default 256KB early-exit caps what it can ever report, silently
// making any configured threshold above 256KB unreachable (#trunc-limit-config).
const estimatedSize = estimateSizeFast(value, maxBodyBytes);
if (estimatedSize <= maxBodyBytes) return value as Record<string, unknown>;
// Object is too large — return a summary instead of a deep clone
Expand All @@ -88,6 +91,10 @@ export function truncateForLog(value: unknown): Record<string, unknown> | null |
if (typeof obj.model === "string") summary.model = obj.model;
if (typeof obj.provider === "string") summary.provider = obj.provider;
if (Array.isArray(obj.messages)) summary.messageCount = obj.messages.length;
// Responses API (`input`, not `messages`) — same count semantics, needed so
// the dashboard's multi-row conversation transcript can still render a
// "N messages not shown" placeholder for a truncated /v1/responses request.
else if (Array.isArray(obj.input)) summary.messageCount = obj.input.length;
if (Array.isArray(obj.contents)) summary.contentCount = obj.contents.length;
if (typeof obj.stream === "boolean") summary.stream = obj.stream;
if (Array.isArray(obj.tools)) summary.tools = cloneBoundedChatLogPayload(obj.tools);
Expand Down
Loading
Loading