fix(codex-bridge): audit findings: visible permanent errors, stricter model command, unbuffered stream

From ten independent read-only audits (11 Oct 2026), verified against the code:
- a permanent gateway error (no tools, 401/402/403, bare upstream 400) ends the turn with a visible
  assistant message instead of a silent retry and an empty turn;
- "modeli ... yapma", "X modeli için test yap" and general "hangi model ..." questions are no longer
  commands; a name whose version does not fit, or that several models share, is asked back;
- the stream is forwarded as it arrives (read1) and an upstream drop is no longer logged as a client
  stop nor saved as an interrupted answer;
- a lone "." joining a running turn is removed without the hook messages after it;
- the interrupted-answer note finds the user message behind hook messages and detects completion
  by event type; a per-minute rate limit is not learned as a context window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
yilsemandClaude Opus 5.5 committed 2026-10-11 01:23:07 +03:00
1 parent 1e2b8e2b9c
commit e2793fa38f
7 files changed
+129 -32

No files matched your search

@@ -17,7 +17,7 @@ for node in tree.body:
if isinstance(node, ast.Assign) and any(isinstance(t, ast.Name) and t.id in
{"ID_PREFIX", "FOREIGN_ID_MARK", "REFERENCE_TYPES", "INLINE_TYPES", "ARAC_OGELERI"} for t in node.targets):
nodes.insert(0, node)
env = {"json": json, "re": __import__("re"), "log": lambda message: None, "pencere": pencere}
env = {"json": json, "re": __import__("re"), "time": __import__("time"), "log": lambda message: None, "pencere": pencere}
exec(compile(ast.Module(body=nodes, type_ignores=[]), "bridge.py", "exec"), env)
NANO = pencere.nano_sadelestir({"data": [
@@ -196,9 +196,13 @@ class GatewayErrors(unittest.TestCase):
adapter = env["SseRewriter"](set())
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 402"}})
out = parse(adapter.feed(raw) + adapter.flush())
self.assertEqual(out[0]["type"], "response.failed")
self.assertEqual(out[0]["response"]["error"]["code"], "insufficient_quota")
self.assertIn("upstream 402", out[0]["response"]["error"]["message"])
self.assertEqual(out[-1]["type"], "response.completed") # visible answer, turn ends normally
self.assertIn("kredi ya da kota", out[1]["delta"])
self.assertIn("upstream 402", out[1]["delta"])
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 400"}})
out = parse(env["SseRewriter"](set()).feed(raw))
self.assertIn("nedenini bildirmedi", out[1]["delta"])
self.assertEqual(out[-1]["type"], "response.completed")
# A transient error is left alone so that Codex retries it.
raw = sse({"type": "error", "error": {"type": "upstream_error", "message": "upstream 503"}})
self.assertEqual(parse(env["SseRewriter"](set()).feed(raw))[0]["type"], "error")
@@ -238,6 +242,11 @@ class ClaudeOrani(unittest.TestCase):
self.assertEqual((trimmed, fits), (1, True))
self.assertLess(estimate, 194_000)
self.assertTrue(p.kapali_hata_mi("upstream 400"))
# A per-minute rate limit is neither an overflow nor a window size to learn.
rate = "Rate limit reached: limit of 32768 tokens per minute, context window ok"
self.assertFalse(p.tasma_mi(rate))
self.assertIsNone(p.sinir_oku(rate))
self.assertTrue(p.tasma_mi("maximum context length is 65536 tokens"))
self.assertFalse(p.kapali_hata_mi("upstream 400: invalid tool schema"))
if __name__ == "__main__":