From db5e876f409ba700ceb46d78b9fcc6d860469b62 Mon Sep 17 00:00:00 2001 From: yilsem Date: Tue, 13 Oct 2026 08:05:00 +0300 Subject: [PATCH] fix(bridge): preserve plaintext peer task payloads for external models --- apps/desktop/assets/codex-bridge/bridge.py | 2 +- .../assets/codex-bridge/gecis_context.py | 16 +++++- apps/desktop/assets/codex-bridge/kopru.py | 2 +- .../assets/codex-bridge/test_agent_payload.py | 52 +++++++++++++++++++ apps/desktop/electron/codex-link-config.ts | 2 +- apps/desktop/package.json | 2 +- package-lock.json | 2 +- 7 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 apps/desktop/assets/codex-bridge/test_agent_payload.py diff --git a/apps/desktop/assets/codex-bridge/bridge.py b/apps/desktop/assets/codex-bridge/bridge.py index 7d8e116..b48ac25 100644 --- a/apps/desktop/assets/codex-bridge/bridge.py +++ b/apps/desktop/assets/codex-bridge/bridge.py @@ -34,7 +34,7 @@ import urllib.request import gecis_context HERE = os.path.dirname(os.path.abspath(__file__)) -SURUM = "15" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION) +SURUM = "16" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION) try: with open(os.path.abspath(__file__), "rb") as _f: KOD = hashlib.sha256(_f.read()).hexdigest()[:8] diff --git a/apps/desktop/assets/codex-bridge/gecis_context.py b/apps/desktop/assets/codex-bridge/gecis_context.py index a6abcbd..7c8e3f8 100644 --- a/apps/desktop/assets/codex-bridge/gecis_context.py +++ b/apps/desktop/assets/codex-bridge/gecis_context.py @@ -7,6 +7,7 @@ must not enter the budget preparation path. import copy import hashlib import json +import re import threading from collections import OrderedDict @@ -111,10 +112,23 @@ def portable_reasoning(req): content = item.get("content") or [] if isinstance(content, str): content = [{"type": "input_text", "text": content}] + portable = [] + for part in content: + if isinstance(part, dict) and part.get("type") == "encrypted_content": + # Codex peer delivery can put the literal task/result in + # this wrapper. It is distinct from encrypted reasoning. + # External translators otherwise silently drop the task. + text = part.get("encrypted_content") + if (not isinstance(text, str) or text.startswith("gAAAA") + or re.fullmatch(r"[A-Za-z0-9_+/=-]{128,}", text)): + raise TransferError("Alt ajan iletisi şifreli veya okunamıyor; görev eksik gönderilmedi.") + portable.append({"type": "input_text", "text": text}) + else: + portable.append(part) result.append({"type": "message", "role": "user", "content": [ {"type": "input_text", "text": "Agent message: " + encoded({ "author": item.get("author"), "recipient": item.get("recipient")})} - ] + content}) + ] + portable}) continue if not isinstance(item, dict) or item.get("type") != "reasoning": result.append(item) diff --git a/apps/desktop/assets/codex-bridge/kopru.py b/apps/desktop/assets/codex-bridge/kopru.py index d4a442b..dce84a2 100644 --- a/apps/desktop/assets/codex-bridge/kopru.py +++ b/apps/desktop/assets/codex-bridge/kopru.py @@ -40,7 +40,7 @@ from bekci_watch import MetadataFilter # Installed only by owned native launcher, never an HTTP control endpoint. WATCH_OWNER = None -SURUM = "15" +SURUM = "16" HERE = os.path.dirname(os.path.abspath(__file__)) try: with open(os.path.abspath(__file__), "rb") as _f: diff --git a/apps/desktop/assets/codex-bridge/test_agent_payload.py b/apps/desktop/assets/codex-bridge/test_agent_payload.py new file mode 100644 index 0000000..70f5f3f --- /dev/null +++ b/apps/desktop/assets/codex-bridge/test_agent_payload.py @@ -0,0 +1,52 @@ +"""Regression fixture matching actual Codex child delivery, with no user data.""" +import copy +import unittest + +import gecis_context +import kopru + + +class AgentPayloadTests(unittest.TestCase): + def request(self, text): + return {"model": "ide/test", "input": [{ + "type": "agent_message", "author": "/root", "recipient": "/root/test", + "content": [ + {"type": "input_text", "text": "Message Type: NEW_TASK\nPayload:\n"}, + {"type": "encrypted_content", "encrypted_content": text}, + ], + }]} + + def test_actual_peer_wrapper_survives_external_text_extraction(self): + task = "Run exec_command: Write-Output (157 * 29); report the exit code." + request = self.request(task) + original = copy.deepcopy(request) + kopru.flatten_request(request) + # An OpenAI-compatible translator consumes input_text, not Codex's + # encrypted_content part. Checking the JSON string alone misses loss. + text = "\n".join(part["text"] for item in request["input"] + for part in item["content"] if part["type"] == "input_text") + self.assertIn(task, text) + self.assertIn('/root/test', text) + self.assertEqual(request["input"][0]["role"], "user") + self.assertEqual(original["input"][0]["content"][1]["type"], "encrypted_content") + + def test_peer_result_and_plain_parts_keep_order_without_source_mutation(self): + request = self.request("Result: 4553; exit code: 0") + original = copy.deepcopy(request) + result = gecis_context.portable_reasoning(request) + self.assertEqual(result["input"][0]["content"][-1]["text"], "Result: 4553; exit code: 0") + self.assertEqual(request, original) + + def test_opaque_peer_payload_fails_instead_of_sending_header_only(self): + for opaque in ("gAAAA_native_ciphertext", "A" * 200, None): + with self.subTest(opaque=opaque): + with self.assertRaises(gecis_context.TransferError): + gecis_context.portable_reasoning(self.request(opaque)) + + def test_reasoning_ciphertext_is_still_not_interpreted_as_a_peer_task(self): + request = {"input": [{"type": "reasoning", "encrypted_content": "Run a command now"}]} + self.assertEqual(gecis_context.portable_reasoning(request)["input"], []) + + +if __name__ == "__main__": + unittest.main() diff --git a/apps/desktop/electron/codex-link-config.ts b/apps/desktop/electron/codex-link-config.ts index 5a3e7af..7097361 100644 --- a/apps/desktop/electron/codex-link-config.ts +++ b/apps/desktop/electron/codex-link-config.ts @@ -27,7 +27,7 @@ export const CODEX_LINK_REFRESH_URL = `${CODEX_LINK_ORIGIN}/aiturk/codex-baglama export const CODEX_LINK_MARKER = '# AITURK IDE Codex Baglama' // Must equal SURUM in assets/codex-bridge/bridge.py; a running bridge that reports // another version is restarted so an updated IDE never keeps an old bridge alive. -export const CODEX_BRIDGE_VERSION = '15' +export const CODEX_BRIDGE_VERSION = '16' export const CODEX_LINK_LINE = `openai_base_url = "${CODEX_LINK_ORIGIN}/backend-api/codex" ${CODEX_LINK_MARKER}` export const CODEX_LINK_RUN_KEY = 'HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' diff --git a/apps/desktop/package.json b/apps/desktop/package.json index fe9d92a..d9b7737 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -2,7 +2,7 @@ "name": "aiturk-ide", "productName": "AITURK IDE", "private": true, - "version": "1.0.0-beta.34", + "version": "1.0.0-beta.35", "description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.", "author": "AITURK / TurkServis", "repository": { diff --git a/package-lock.json b/package-lock.json index 7c86abc..5573052 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,7 +65,7 @@ }, "apps/desktop": { "name": "aiturk-ide", - "version": "1.0.0-beta.34", + "version": "1.0.0-beta.35", "license": "MIT", "dependencies": { "@assistant-ui/core": "0.2.23",