Files
granthi-review/src/lib/router.js
T
Nirav PatelandClaude Fable 5 f7b56297a8 fix: raw:true bypass for router keyword fast-paths + c-suite fallback agent
Two live-observed router hijack modes on diff content: (1) retail/store
fast-paths (store-resolver, arm:sales) intercepting prompts whose diffs
mention sales/store-ish words, (2) domain preflight 400 agent_mismatch
when the intent classifier confidently misclassifies a diff. raw:true
(_bypassToolRouting) disables the keyword intercepts; attempt 2 retries
as the configurable c-suite fallback agent (architect) which bypasses
the tier-gated domain preflight.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-19 00:00:37 -04:00

35 lines
1.7 KiB
JavaScript

// shre-router client. POST /v1/chat with stream:false.
// Real model answer lives in .message.content; top-level .content can carry an
// upstream-down apology — always prefer .message.content.
export async function chat({ routerUrl, model, messages, agentId, sessionId, tenantId, timeoutMs }) {
const res = await fetch(`${routerUrl.replace(/\/$/, '')}/v1/chat`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
// tools:false + raw:true — reviews need pure model reasoning. raw:true is
// the router's sanctioned _bypassToolRouting flag: without it, keyword
// fast-paths (current-info briefing, store-resolver, retail dispatch)
// hijack prompts whose DIFF CONTENT mentions github/sales/store-ish terms
// and return non-review text. The domain preflight (agent_mismatch 400)
// still applies per agent tier; the pipeline's attempt-2 fallback agent
// covers that.
body: JSON.stringify({ model, messages, agentId, sessionId, tenantId, stream: false, tools: false, raw: true }),
signal: AbortSignal.timeout(timeoutMs)
});
if (!res.ok) {
const text = await res.text().catch(() => '');
throw new Error(`router -> ${res.status}: ${text.slice(0, 300)}`);
}
const data = await res.json();
// Response shape varies by upstream: prefer .message.content, then
// OpenAI-style choices, then top-level .content (which can carry an
// upstream-down apology — hence last).
const content = data?.message?.content
?? data?.choices?.[0]?.message?.content
?? data?.content;
if (typeof content !== 'string' || !content.trim()) {
throw new Error('router returned empty content');
}
return { content, servedModel: data?._shre?.model ?? null };
}