From 7d0a36d393da13226951df6269ca2ed26b6322c8 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 9 Oct 2026 06:19:01 -0500 Subject: [PATCH 1/2] test(ui): dismiss late announcements during company navigation Retry the normal Agents link within a bounded deadline and use the ordinary dismissal control if the asynchronously loaded announcement covers it. Preserve the company-isolation and history assertions. Co-Authored-By: Paperclip --- tests/e2e/chat-adapters-ui-messaging.spec.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/e2e/chat-adapters-ui-messaging.spec.ts b/tests/e2e/chat-adapters-ui-messaging.spec.ts index 9a73e722a2..685133dbc6 100644 --- a/tests/e2e/chat-adapters-ui-messaging.spec.ts +++ b/tests/e2e/chat-adapters-ui-messaging.spec.ts @@ -1364,8 +1364,13 @@ test.describe("Exact failed chat run retry", () => { afterSwitchSelectedCompany: await page.evaluate(() => localStorage.getItem("paperclip.selectedCompanyId")), }, null, 2)); await expect(page).toHaveURL(new RegExp(`/${seed.prefix}/dashboard$`)); - if (await dismissAnnouncement.isVisible()) await dismissAnnouncement.click(); - await page.getByRole("link", { name: "Agents", exact: true }).first().click(); + // The announcement waits for its feed and a settle timer. It can appear + // after the dashboard URL settles, while the sidebar click is retrying. + // Dismiss it through the normal control when it actually blocks the link. + await expect(async () => { + if (await dismissAnnouncement.isVisible()) await dismissAnnouncement.click(); + await page.getByRole("link", { name: "Agents", exact: true }).first().click({ timeout: 1_000 }); + }).toPass({ timeout: 10_000 }); await page.getByRole("link", { name: "Maya", exact: true }).first().click(); await expect(page).toHaveURL(new RegExp(`/${seed.prefix}/agents/maya(?:/overview)?$`)); await expect(page.getByText(foreignTitle, { exact: true })).toHaveCount(0); From dc53d4d6b7a68bef59a9438337f0ce6cd874eca2 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 9 Oct 2026 06:19:04 -0500 Subject: [PATCH 2/2] fix(hermes): retain reported charges from interrupted requests Keep authenticated verified OpenRouter usage frames in the known subtotal even when transport fails, without certifying complete cost or token totals. Cover the real pinned SDK interruption plus a successful retry and update both reviewed runtime closure pins. Co-Authored-By: Paperclip --- .../src/drivers/acpx/hermes-distributions.ts | 4 +- .../src/providers/hermes/billing.py | 12 +++++- .../src/providers/hermes/test_billing.py | 39 ++++++++++++++++++- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/packages/paperclip-runner/src/drivers/acpx/hermes-distributions.ts b/packages/paperclip-runner/src/drivers/acpx/hermes-distributions.ts index 876624b472..b92812aa14 100644 --- a/packages/paperclip-runner/src/drivers/acpx/hermes-distributions.ts +++ b/packages/paperclip-runner/src/drivers/acpx/hermes-distributions.ts @@ -1,5 +1,5 @@ /** Reviewed native closures. Setup cannot adopt a digest from downloaded files. */ export const HERMES_CLOSURES: Readonly> = Object.freeze({ - "darwin-arm64": "26371563768eb6578b80019f86d4a49cb55e36fd1593021cf9eac2cba945180c", - "linux-x64": "06a4e2d0005f0600125b8b3032cb200d7d5ff21280ecd6c3b80f29d29a3491a8", + "darwin-arm64": "fdf1369e1713fcf6dbaf43bb4a08e2cb819120bfef47d6047adfe6f7f47778a1", + "linux-x64": "62f0071c5be0b5d07088b0d1f9d280db64390f7aeab395a526059afaa3573135", }); diff --git a/packages/paperclip-runner/src/providers/hermes/billing.py b/packages/paperclip-runner/src/providers/hermes/billing.py index 15fe1b868f..26c65c59df 100644 --- a/packages/paperclip-runner/src/providers/hermes/billing.py +++ b/packages/paperclip-runner/src/providers/hermes/billing.py @@ -80,13 +80,15 @@ class TurnBilling: with self._lock: self._closed = True completed = [r for r in self._requests if r.valid and r.done] - priced = [r for r in completed if r.cost is not None] + # A verified usage frame can precede a transport interruption. + # Keep that known charge without certifying the request's totals. + priced = [r for r in self._requests if r.cost_verified] tokenized = [r for r in completed if r.tokens is not None] scope_complete = owned_work_complete and not self._overflow and not self._children cost = sum(r.cost for r in priced) if cost > 1000000000000000: raise ValueError("Hermes reported turn cost exceeds its receipt bound") - complete = scope_complete and len(priced) == len(self._requests) + complete = scope_complete and len(completed) == len(self._requests) and len(priced) == len(self._requests) token_complete = scope_complete and len(tokenized) == len(self._requests) totals = tuple(sum(r.tokens[i] for r in tokenized) for i in range(4)) if any(value > 9007199254740991 for value in totals): @@ -107,6 +109,7 @@ class WireReceipt: self.done = False self.tokens = None self.cost = None + self.cost_verified = False self._seen_usage = False self._buffer = bytearray() self._data = [] @@ -117,9 +120,11 @@ class WireReceipt: value = json.loads(data, parse_float=Decimal) except (ValueError, UnicodeError): self.valid = False + self.cost_verified = False return if not isinstance(value, dict): self.valid = False + self.cost_verified = False return usage = value.get("usage") if usage is not None: @@ -128,13 +133,16 @@ class WireReceipt: self.valid = False self._seen_usage = True self.tokens, self.cost = tokens, cost + self.cost_verified = self.valid and cost is not None if value.get("error") is not None: self.valid = False + self.cost_verified = False def json(self, body): with self.owner._lock: if len(body) > MAX_BODY: self.valid = False + self.cost_verified = False else: self._document(body) self.done = True diff --git a/packages/paperclip-runner/src/providers/hermes/test_billing.py b/packages/paperclip-runner/src/providers/hermes/test_billing.py index 501d1a4de9..66bd96fc22 100644 --- a/packages/paperclip-runner/src/providers/hermes/test_billing.py +++ b/packages/paperclip-runner/src/providers/hermes/test_billing.py @@ -80,7 +80,19 @@ class Accounting(unittest.TestCase): self.assertFalse(ledger.finish()[0]["complete"]) ledger = TurnBilling(); receipt = ledger.begin() receipt.json(document()); receipt.json(document("0.01")) - self.assertFalse(ledger.finish()[0]["complete"]) + billed, _ = ledger.finish() + self.assertFalse(billed["complete"]) + self.assertEqual(billed["reportedRequestCount"], 0) + self.assertEqual(billed["amountUsdExact"], "0.000000000") + + def test_verified_frame_before_an_unfinished_response_is_a_known_subtotal(self): + ledger = TurnBilling(); receipt = ledger.begin() + receipt.feed(b"data: " + document() + b"\n\n") + billed, tokens = ledger.finish() + self.assertEqual(billed["reportedRequestCount"], 1) + self.assertEqual(billed["amountUsdExact"], "0.004200000") + self.assertFalse(billed["complete"]) + self.assertIsNone(tokens) def test_truncation_and_oversized_frames_are_unpriced(self): for wire in (b"data: " + document() + b"\n", b"data: " + b"x" * (MAX_FRAME + 1)): @@ -130,6 +142,31 @@ class Transport(unittest.TestCase): self.assertFalse(billed["complete"]) self.assertIsNone(tokens) + def test_interrupted_stream_keeps_its_verified_charge_before_a_successful_retry(self): + class InterruptedStream(httpx.SyncByteStream): + def __iter__(self): + yield b"data: " + document() + b"\n\n" + raise httpx.ReadError("synthetic interrupted stream") + + calls = [] + def reply(request): + calls.append(request) + if len(calls) == 1: + return httpx.Response(200, headers={"content-type": "text/event-stream"}, stream=InterruptedStream()) + return httpx.Response(200, content=document()) + + with httpx.Client(transport=httpx.MockTransport(reply)) as client, OpenAI(http_client=client, api_key="synthetic-fixture-only", base_url="https://openrouter.ai/api/v1") as sdk: + with self.assertRaises(httpx.ReadError): + with sdk.chat.completions.create(model="fixture-model", messages=[], stream=True) as stream: + list(stream) + sdk.chat.completions.create(model="fixture-model", messages=[]) + billed, tokens = self.ledger.finish() + self.assertEqual(billed["requestCount"], 2) + self.assertEqual(billed["reportedRequestCount"], 2) + self.assertEqual(billed["amountUsdExact"], "0.008400000") + self.assertFalse(billed["complete"]) + self.assertIsNone(tokens) + def test_account_or_endpoint_changes_cannot_mint_billing_for_the_selected_account(self): for url, key in [("https://other.test/v1/chat/completions", "synthetic-fixture-only"), ("https://openrouter.ai/api/v1/chat/completions", "another-fixture-account"),