diff --git a/apps/hub/src/index.ts b/apps/hub/src/index.ts
index 8a467fd92..b113c9a34 100644
--- a/apps/hub/src/index.ts
+++ b/apps/hub/src/index.ts
@@ -2216,9 +2216,10 @@ export async function createHub(config: HubConfig) {
// A connection completing through ANY door below — OAuth callback,
// pasted key, MCP OAuth, keyless MCP preset — settles every room
// waiting on that connector: the room's `connections/pending` entry
- // clears (flipping the in-room connect card via `chat.settings`), and
- // the host agent is woken via `dispatchTurn` without a forged
- // signed-in-user timeline row.
+ // clears (flipping the in-room connect card via `chat.settings`).
+ // Rooms with `connections/pending` still wake the host agent via
+ // `dispatchTurn` without a forged signed-in-user timeline row;
+ // code-review template rooms are not woken on PAT settle.
//
// An inference provider's credential landing also re-checks every
// live participant's deployed inference chain (CL-6687): a rotated
diff --git a/packages/chat-ui/src/inference-failure.test.ts b/packages/chat-ui/src/inference-failure.test.ts
index 80c9326d6..5ba8784b3 100644
--- a/packages/chat-ui/src/inference-failure.test.ts
+++ b/packages/chat-ui/src/inference-failure.test.ts
@@ -1,26 +1,23 @@
import { describe, expect, test } from "bun:test";
import {
+ CONSUMER_INFERENCE_FAILURE_NOTICE,
consumerFacingInferenceText,
isClassifiedInferenceFailureText,
} from "./inference-failure";
describe("consumerFacingInferenceText", () => {
- test("keeps the vendor preamble and drops [HTTP …] plus the raw provider message", () => {
+ test("maps a classified credential failure to one recovery sentence", () => {
expect(
consumerFacingInferenceText(
"This agent could not complete your request due to a credential error [HTTP 401]: API key is invalid",
),
- ).toBe(
- "This agent could not complete your request due to a credential error",
- );
+ ).toBe(CONSUMER_INFERENCE_FAILURE_NOTICE);
expect(
consumerFacingInferenceText(
"This agent could not complete your request because the API quota has been exhausted [HTTP 429]: rate limited",
),
- ).toBe(
- "This agent could not complete your request because the API quota has been exhausted",
- );
+ ).toBe(CONSUMER_INFERENCE_FAILURE_NOTICE);
});
test("a forced [HTTP 401] dump is not consumer copy", () => {
@@ -47,12 +44,15 @@ describe("isClassifiedInferenceFailureText", () => {
).toBe(true);
});
- test("matches a credential_failure reply after HTTP/raw is stripped", () => {
+ test("a stripped credential_failure reply is recovery copy, not the classified preamble", () => {
+ const facing = consumerFacingInferenceText(
+ "This agent could not complete your request due to a credential error [HTTP 401]: invalid api key",
+ );
+ expect(facing).toBe(CONSUMER_INFERENCE_FAILURE_NOTICE);
+ expect(facing.toLowerCase()).not.toContain("credential error");
expect(
isClassifiedInferenceFailureText(
- consumerFacingInferenceText(
- "This agent could not complete your request due to a credential error [HTTP 401]: invalid api key",
- ),
+ "This agent could not complete your request due to a credential error [HTTP 401]: invalid api key",
),
).toBe(true);
});
diff --git a/packages/chat-ui/src/inference-failure.ts b/packages/chat-ui/src/inference-failure.ts
index 0b3bb35a2..65906aab6 100644
--- a/packages/chat-ui/src/inference-failure.ts
+++ b/packages/chat-ui/src/inference-failure.ts
@@ -29,7 +29,10 @@ export const CLASSIFIED_INFERENCE_FAILURE_PREAMBLES: readonly string[] = [
"This agent could not complete your request because the API quota has been exhausted",
];
-export { consumerFacingInferenceText } from "@corbits/chat/consumer-inference-text";
+export {
+ CONSUMER_INFERENCE_FAILURE_NOTICE,
+ consumerFacingInferenceText,
+} from "@corbits/chat/consumer-inference-text";
export function isClassifiedInferenceFailureText(text: string): boolean {
return CLASSIFIED_INFERENCE_FAILURE_PREAMBLES.some((preamble) =>
diff --git a/packages/chat-ui/src/timeline.tsx b/packages/chat-ui/src/timeline.tsx
index 6b3eadfea..8d21ffd2a 100644
--- a/packages/chat-ui/src/timeline.tsx
+++ b/packages/chat-ui/src/timeline.tsx
@@ -59,6 +59,7 @@ import type { ConnectGithubActions } from "./blocks/connect-github-actions";
import type { ConnectServiceActions } from "./blocks/connect-service-actions";
import { BlockPartView } from "./blocks/registry";
import {
+ CONSUMER_INFERENCE_FAILURE_NOTICE,
consumerFacingInferenceText,
isClassifiedInferenceFailureText,
} from "./inference-failure";
@@ -551,7 +552,8 @@ function TextBubble({
{onFixConnection !== undefined &&
- isClassifiedInferenceFailureText(consumerText) && (
+ (isClassifiedInferenceFailureText(text) ||
+ consumerText === CONSUMER_INFERENCE_FAILURE_NOTICE) && (