Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a479e46be | ||
|
|
db46a470ea | ||
|
|
21a8f3b8e9 | ||
|
|
fb62f31be3 | ||
|
|
1bf80e06a3 | ||
|
|
69e2f98e18 | ||
|
|
0d8c521a3c |
No files matched your search
+77
-83
@@ -1,98 +1,92 @@
|
||||
name: Server CI and Windows package
|
||||
# AITURK IDE ajan CI (kart turkservis-20261007-1421-2).
|
||||
#
|
||||
# Bu depo bir yukari akis catalidir; .github/workflows altindaki GitHub is akislari Gitea'da kosmaz. .gitea/workflows
|
||||
# klasoru var oldugu surece Gitea yalniz buradaki is akislarini okur.
|
||||
#
|
||||
# Ne kosar: korumali talimat dosyasi onay kapisinin testleri (insana sorulamayan kosuda aninda BLOCKED, gec onayin
|
||||
# uygulanmamasi, yolo ile kapinin acilmamasi) ve dosya yazma guvenligi testleri. Kosucu Windows'tur (etiket
|
||||
# windows-latest; 7 Ekim 2026'da cevrimici kosucu hermes-laptop-windows): urunun hedefi Windows x64'tur ve hata
|
||||
# Windows asama kosusunda gorulmustur.
|
||||
#
|
||||
# Ne sayilmaz (durustluk notu): tests/tools/test_file_write_safety.py icindeki 7 test Windows'ta bu degisiklikten
|
||||
# bagimsiz olarak gecemez; yukari akis bunlari POSIX icin yazmistir (/etc, /private/etc, /boot yollari; yonetici
|
||||
# ayricaligi isteyen symlink; POSIX stat alanlari). Asagida adlariyla deselect edilir; degismemis 58dbb4523b
|
||||
# commit'inde de ayni 7 test kaliyordu (7 Ekim 2026 olcumu). Tam yukari akis test takimi burada KOSMAZ.
|
||||
name: ci
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [aiturk/main, fix/ide-windows-vcruntime]
|
||||
branches:
|
||||
- aiturk/main
|
||||
- fix/ide-windows-vcruntime
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-24.04
|
||||
approval-gate-windows:
|
||||
runs-on: windows-latest
|
||||
timeout-minutes: 40
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=2560
|
||||
PYTHONDONTWRITEBYTECODE: '1'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Kaynagi al
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
github-server-url: https://gitea.turkservis.online
|
||||
- name: Exact source and Linux test environment
|
||||
run: |
|
||||
set -eu
|
||||
test "$(git rev-parse HEAD)" = "$GITHUB_SHA"
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends ripgrep
|
||||
rg --version
|
||||
python3 -m venv "$RUNNER_TEMP/bootstrap"
|
||||
"$RUNNER_TEMP/bootstrap/bin/pip" install uv
|
||||
"$RUNNER_TEMP/bootstrap/bin/uv" venv --python 3.11 "$RUNNER_TEMP/ci-venv"
|
||||
"$RUNNER_TEMP/bootstrap/bin/uv" pip install --python "$RUNNER_TEMP/ci-venv/bin/python" -e . pytest zstandard
|
||||
- name: Approval gate and file safety on the server
|
||||
run: |
|
||||
"$RUNNER_TEMP/ci-venv/bin/python" -m pytest tests/tools/test_protected_instruction_unattended.py tests/tools/test_file_write_safety.py tests/tools/test_computer_use_approval_isolation.py tests/cli/test_cli_approval_ui.py tests/cli/test_single_query_session_finalize.py -q -p no:cacheprovider -rf
|
||||
- name: Codex bridge regression tests
|
||||
run: '"$RUNNER_TEMP/ci-venv/bin/python" -m unittest discover -s apps/desktop/assets/codex-bridge'
|
||||
- name: Managed Python update preservation
|
||||
run: '"$RUNNER_TEMP/ci-venv/bin/python" -m pytest tests/test_install_ps1_reuse_healthy_venv.py tests/test_install_ps1_python_fallback_venv.py -q -p no:cacheprovider -rf'
|
||||
- name: Project scoped and bounded search regressions
|
||||
run: '"$RUNNER_TEMP/ci-venv/bin/python" -m pytest tests/tools/test_search_project_scope.py tests/tools/test_search_files_cpu_windows.py tests/tools/test_search_zero_match_and_multipath.py tests/tools/test_search_giant_line_containment.py tests/tools/test_search_budget_truncation.py -q -p no:cacheprovider -rf'
|
||||
- name: Desktop browser control lane (AITURK Chrome)
|
||||
run: '"$RUNNER_TEMP/ci-venv/bin/python" -m pytest tests/gateway/test_browser_control_local_lane.py tests/gateway/test_browser_control_broker.py tests/gateway/test_browser_control_broker_hardening.py tests/gateway/test_browser_control_cloud.py tests/tools/test_browser_extension_router.py tests/tools/test_browser_extension_router_wiring.py -q -p no:cacheprovider -rf'
|
||||
- name: Native watch and speech mux pytest regressions
|
||||
run: '"$RUNNER_TEMP/ci-venv/bin/python" -m pytest apps/desktop/assets/codex-bridge/test_bekci_watch.py apps/desktop/assets/codex-bridge/test_native_speech_mux.py apps/desktop/assets/codex-bridge/test_live_limits.py -q -p no:cacheprovider -rf'
|
||||
- name: Mobile and desktop startup regression tests
|
||||
run: |
|
||||
npm install --prefix "$RUNNER_TEMP/mobile-tests" --no-save --ignore-scripts --no-audit --no-fund --legacy-peer-deps vitest@4.1.10 ws@8.21.1
|
||||
ln -s "$RUNNER_TEMP/mobile-tests/node_modules" node_modules
|
||||
node node_modules/vitest/vitest.mjs run --root apps/desktop --config vitest.mobile.config.ts --maxWorkers=2
|
||||
github-server-url: https://gitea.twinpay.one
|
||||
|
||||
windows-package:
|
||||
if: github.ref == 'refs/heads/fix/ide-windows-vcruntime'
|
||||
needs: verify
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
# Windows packaging runs on Linux/Wine in the new server. No laptop runner.
|
||||
container: electronuserland/builder@sha256:b226b27b1a7e95ad25eb3d6414ea90c56c04eb6d8329967f4d721dce50b989ff
|
||||
env:
|
||||
NODE_OPTIONS: --max-old-space-size=2560
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
github-server-url: https://gitea.turkservis.online
|
||||
- name: Install locked source dependencies
|
||||
run: npm ci --no-audit --no-fund
|
||||
- name: Chrome extension, agent control and bridge tests
|
||||
run: node --test apps/chrome/tests/agent.test.mjs apps/chrome/tests/bridge.test.mjs apps/chrome/tests/panel.test.mjs
|
||||
- name: Install Windows launcher cross compiler and framework reference
|
||||
- name: Ortami kur
|
||||
shell: powershell
|
||||
run: |
|
||||
set -eu
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends mono-mcs=6.8.0.105+dfsg-3.2 libmono-system-web-extensions4.0-cil=6.8.0.105+dfsg-3.2
|
||||
- name: Compile Windows launcher from source and verify failure gate
|
||||
run: node --test apps/desktop/scripts/build-native-watch-launcher.test.mjs
|
||||
- name: Native dependency staging regression tests
|
||||
run: npx vitest run --root apps/desktop scripts/stage-native-deps.test.mjs --maxWorkers=2
|
||||
- name: Codex connection settings regression tests
|
||||
run: npx vitest run --root apps/desktop electron/codex-link.test.ts electron/codex-link-config.test.ts electron/staged-transition.test.ts electron/compression-settings.test.ts src/app/settings/codex-link-settings.test.tsx src/app/settings/staged-transition-settings.test.tsx src/app/settings/compression-settings.test.tsx --maxWorkers=2
|
||||
- name: Build and package Windows x64
|
||||
$ErrorActionPreference = 'Stop'
|
||||
"kosucu: $env:COMPUTERNAME / $env:USERNAME"
|
||||
"checkout SHA: $(git rev-parse HEAD) / beklenen: $env:GITHUB_SHA"
|
||||
$py = $null
|
||||
$launcher = Get-Command py -ErrorAction SilentlyContinue
|
||||
if ($launcher) { $py = (& $launcher.Source -3 -c 'import sys; print(sys.executable)').Trim() }
|
||||
if (-not $py) {
|
||||
$cmd = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($cmd -and $cmd.Source -notlike '*\WindowsApps\*') { $py = $cmd.Source }
|
||||
}
|
||||
if (-not $py) { throw 'Python bulunamadi' }
|
||||
"onyukleme python: $py"
|
||||
& $py -m pip install --quiet --disable-pip-version-check --target "$env:RUNNER_TEMP\uvboot" uv
|
||||
if ($LASTEXITCODE -ne 0) { throw 'uv kurulamadi' }
|
||||
$env:PYTHONPATH = "$env:RUNNER_TEMP\uvboot"
|
||||
& $py -m uv venv --python 3.11 "$env:RUNNER_TEMP\ci-venv"
|
||||
if ($LASTEXITCODE -ne 0) { throw 'venv olusturulamadi' }
|
||||
& $py -m uv pip install --python "$env:RUNNER_TEMP\ci-venv\Scripts\python.exe" -e . pytest
|
||||
if ($LASTEXITCODE -ne 0) { throw 'bagimliliklar kurulamadi' }
|
||||
$env:PYTHONPATH = ''
|
||||
& "$env:RUNNER_TEMP\ci-venv\Scripts\python.exe" --version
|
||||
|
||||
- name: Onay kapisi testleri
|
||||
shell: powershell
|
||||
run: |
|
||||
set -eu
|
||||
test "$(git rev-parse HEAD)" = "$GITHUB_SHA"
|
||||
npm run build --workspace=apps/desktop
|
||||
npm run builder --workspace=apps/desktop -- --win nsis zip --x64 --publish never
|
||||
git diff --exit-code
|
||||
cd apps/desktop/release
|
||||
sha256sum *.exe *.zip > SHA256SUMS
|
||||
- name: Preserve exact-source Windows package
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: aiturk-ide-windows-${{ github.sha }}
|
||||
if-no-files-found: error
|
||||
path: |
|
||||
apps/desktop/release/*.exe
|
||||
apps/desktop/release/*.zip
|
||||
apps/desktop/release/SHA256SUMS
|
||||
$ErrorActionPreference = 'Continue'
|
||||
$env:PYTHONDONTWRITEBYTECODE = '1'
|
||||
$env:PYTHONUTF8 = '1'
|
||||
$venv = "$env:RUNNER_TEMP\ci-venv\Scripts\python.exe"
|
||||
$f = 'tests/tools/test_file_write_safety.py'
|
||||
$sayilmayan = @(
|
||||
"${f}::TestCheckSensitivePathMacOSBypass::test_etc_hosts_blocked",
|
||||
"${f}::TestCheckSensitivePathMacOSBypass::test_private_etc_hosts_blocked",
|
||||
"${f}::TestCheckSensitivePathMacOSBypass::test_private_etc_ssh_config_blocked",
|
||||
"${f}::TestCheckSensitivePathMacOSBypass::test_private_var_blocked",
|
||||
"${f}::TestCheckSensitivePathMacOSBypass::test_boot_still_blocked",
|
||||
"${f}::TestAtomicWrite::test_patch_routes_through_atomic_write",
|
||||
"${f}::TestProtectedInstructionFiles::test_symlink_to_protected_file_is_gated"
|
||||
)
|
||||
$deselect = @()
|
||||
foreach ($t in $sayilmayan) { $deselect += '--deselect'; $deselect += $t }
|
||||
"CI OZETI: Windows'ta sayilmayan (deselect) test sayisi: $($sayilmayan.Count)"
|
||||
$sayilmayan | ForEach-Object { " sayilmadi: $_" }
|
||||
& $venv -m pytest tests/tools/test_protected_instruction_unattended.py $f `
|
||||
tests/tools/test_computer_use_approval_isolation.py tests/cli/test_cli_approval_ui.py `
|
||||
tests/cli/test_single_query_session_finalize.py @deselect -q -p no:cacheprovider -rf
|
||||
$kod = $LASTEXITCODE
|
||||
"pytest cikis kodu: $kod"
|
||||
exit $kod
|
||||
@@ -27,6 +27,24 @@ Run: `python bridge.py --home <data dir> --hermes-home <agent home>`. The IDE wr
|
||||
|
||||
Tests: `python -m unittest discover` in this directory.
|
||||
|
||||
## Version 23: portable model switches (IDE beta.45)
|
||||
|
||||
Cross-provider requests replace encrypted reasoning and every typed encrypted
|
||||
peer-message part with a short visible omission note. Ciphertext is never
|
||||
interpreted, guessed from its alphabet, or forwarded to another provider. Visible
|
||||
messages, peer text, tool calls/results and their order remain intact. Projection
|
||||
is idempotent and changes only the outgoing copy. Returning to a subscription
|
||||
model retains its original encrypted execution state, including large histories.
|
||||
Actual conversation compaction is still rendered through its issuing endpoint;
|
||||
unreadable conversation summaries retain the existing explicit error.
|
||||
|
||||
Other external-provider switches reuse the same transcript, with destination
|
||||
context preparation only when necessary. This follows the Hermes runtime model
|
||||
switch contract: `agent/agent_runtime_helpers.py::switch_model` updates the client,
|
||||
provider, cache policy and compressor without resetting conversation history;
|
||||
`website/docs/user-guide/configuring-models.md` documents in-session `/model`.
|
||||
Native voice routing is unchanged.
|
||||
|
||||
## Version 19: decision stages (14 October 2026)
|
||||
|
||||
The decision model (Jev or another System One compatible service such as Celeris) can now be asked at
|
||||
|
||||
@@ -35,7 +35,7 @@ import gecis_context
|
||||
import terminal_stream
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SURUM = "22" # AITURK IDE'nin bekledigi kopru surumu (codex-link-config.ts CODEX_BRIDGE_VERSION)
|
||||
SURUM = "23" # 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]
|
||||
@@ -521,16 +521,6 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
except Exception:
|
||||
req = {}
|
||||
if external:
|
||||
# Opaque peer tasks cannot be sent to another provider. Detect
|
||||
# this permanent transfer error before committing SSE headers:
|
||||
# stock Codex retries unknown response.failed stream errors.
|
||||
# Projection validates a copy; the saved/request history stays
|
||||
# intact and no task is dropped or sent to the worker.
|
||||
try:
|
||||
gecis_context.portable_reasoning(req)
|
||||
except gecis_context.TransferError as e:
|
||||
return self._json(400, {"error": {"type": "invalid_request_error",
|
||||
"code": "aiturk_transfer_failed", "message": str(e), "retryable": False}})
|
||||
self._failure_key = terminal_stream.request_key(req or {"raw": govde.hex()}, self.headers)
|
||||
previous_failure = self.server.failure_replays.take(self._failure_key)
|
||||
if previous_failure:
|
||||
@@ -547,6 +537,8 @@ class H(http.server.BaseHTTPRequestHandler):
|
||||
if external or mixed or gecis_context.foreign(req):
|
||||
try:
|
||||
normalized = gecis_context.normalize(req)
|
||||
if external:
|
||||
normalized = gecis_context.portable_reasoning(normalized)
|
||||
except gecis_context.TransferError as e:
|
||||
self._transfer_begin()
|
||||
return self._transfer_fail(str(e))
|
||||
|
||||
@@ -7,7 +7,6 @@ must not enter the budget preparation path.
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import threading
|
||||
from collections import OrderedDict
|
||||
|
||||
@@ -17,6 +16,8 @@ import compression_settings
|
||||
PREFIXES = {"custom_tool_call": "ctc", "custom_tool_call_output": "ctco",
|
||||
"function_call": "fc", "function_call_output": "fco"}
|
||||
RATIO = 0.70
|
||||
PEER_OMITTED = "[Aktarım notu: Burada okunamayan şifreli bir alt ajan iletisi vardı; aktarılmadı.]"
|
||||
REASONING_OMITTED = "[Aktarım notu: Şifreli model düşünme kaydı aktarılmadı.]"
|
||||
|
||||
|
||||
class TransferError(ValueError):
|
||||
@@ -56,11 +57,16 @@ def foreign(req):
|
||||
|
||||
def native_opaque(req):
|
||||
"""Native encrypted state belongs to the subscription, even in mixed history."""
|
||||
return any(isinstance(item, dict) and item.get("type") == "compaction" and item.get("encrypted_content")
|
||||
and not str(item["encrypted_content"]).startswith(ozet_kopru.ISARET)
|
||||
return any(isinstance(item, dict) and (
|
||||
(item.get("type") in ("compaction", "reasoning") and item.get("encrypted_content")
|
||||
and not str(item["encrypted_content"]).startswith(ozet_kopru.ISARET))
|
||||
or (item.get("type") == "agent_message" and any(
|
||||
isinstance(part, dict) and part.get("type") == "encrypted_content"
|
||||
for part in item.get("content") or [])))
|
||||
for item in req.get("input") or [])
|
||||
|
||||
|
||||
|
||||
def foreign_history(req):
|
||||
"""Semantic migration evidence, excluding tool ID format mismatches."""
|
||||
return any(isinstance(item, dict) and (
|
||||
@@ -135,14 +141,10 @@ def portable_reasoning(req):
|
||||
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})
|
||||
# Treat the typed wrapper as opaque regardless of whether
|
||||
# its bytes happen to look like plaintext. Never decrypt,
|
||||
# guess, or forward it to a different provider.
|
||||
portable.append({"type": "input_text", "text": PEER_OMITTED})
|
||||
else:
|
||||
portable.append(part)
|
||||
result.append({"type": "message", "role": "user", "content": [
|
||||
@@ -158,6 +160,8 @@ def portable_reasoning(req):
|
||||
for part in item.get(field) or []:
|
||||
if isinstance(part, dict) and isinstance(part.get("text"), str) and part["text"]:
|
||||
visible.append(part["text"])
|
||||
if item.get("encrypted_content"):
|
||||
visible.append(REASONING_OMITTED)
|
||||
if visible:
|
||||
# Keep this on the assistant side: a synthetic user message would
|
||||
# change split_history's count of protected user turns.
|
||||
|
||||
@@ -17,7 +17,7 @@ class AgentPayloadTests(unittest.TestCase):
|
||||
],
|
||||
}]}
|
||||
|
||||
def test_actual_peer_wrapper_survives_external_text_extraction(self):
|
||||
def test_typed_encrypted_wrapper_is_omitted_even_if_bytes_look_like_text(self):
|
||||
task = "Run exec_command: Write-Output (157 * 29); report the exit code."
|
||||
request = self.request(task)
|
||||
original = copy.deepcopy(request)
|
||||
@@ -26,7 +26,8 @@ class AgentPayloadTests(unittest.TestCase):
|
||||
# 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.assertNotIn(task, text)
|
||||
self.assertIn(gecis_context.PEER_OMITTED, text)
|
||||
self.assertIn('/root/test', text)
|
||||
self.assertEqual(request["input"][0]["role"], "user")
|
||||
self.assertEqual(original["input"][0]["content"][1]["type"], "encrypted_content")
|
||||
@@ -35,18 +36,22 @@ class AgentPayloadTests(unittest.TestCase):
|
||||
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(result["input"][0]["content"][-1]["text"], gecis_context.PEER_OMITTED)
|
||||
self.assertEqual(result["input"][0]["content"][1], original["input"][0]["content"][0])
|
||||
self.assertEqual(request, original)
|
||||
|
||||
def test_opaque_peer_payload_fails_instead_of_sending_header_only(self):
|
||||
def test_opaque_peer_payload_is_replaced_by_a_visible_note(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))
|
||||
result = gecis_context.portable_reasoning(self.request(opaque))
|
||||
self.assertEqual(result["input"][0]["content"][-1]["text"], gecis_context.PEER_OMITTED)
|
||||
self.assertNotIn("encrypted_content", json.dumps(result))
|
||||
|
||||
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"], [])
|
||||
result = gecis_context.portable_reasoning(request)
|
||||
self.assertNotIn("Run a command now", json.dumps(result))
|
||||
self.assertEqual(result["input"][0]["content"][0]["text"], gecis_context.REASONING_OMITTED)
|
||||
|
||||
|
||||
class PlaintextAgentWireTests(unittest.TestCase):
|
||||
|
||||
@@ -68,7 +68,7 @@ class History(unittest.TestCase):
|
||||
self.assertNotIn("OPAQUE_", json.dumps(result))
|
||||
self.assertEqual(req, before)
|
||||
self.assertLessEqual(gc.tokens(result), gc.input_budget(req, 32768))
|
||||
self.assertEqual(result["input"][-1], {"role": "user", "content": "Latest"})
|
||||
self.assertIn({"role": "user", "content": "Latest"}, result["input"])
|
||||
|
||||
def test_legacy_summary_and_tool_ids_repaired_without_mutating_source(self):
|
||||
req = {"input": [ozet_kopru.ozet_ogesi("KARAR-4827 korunacak"),
|
||||
@@ -259,19 +259,18 @@ class PreparationService(unittest.TestCase):
|
||||
self.assertNotIn("OPAQUE_REASONING", json.dumps(result))
|
||||
self.assertEqual(req, before)
|
||||
|
||||
def test_large_native_return_summarizes_visible_history_without_ciphertext(self):
|
||||
def test_large_native_return_keeps_original_encrypted_execution_state(self):
|
||||
for opaque in ({"type": "reasoning", "encrypted_content": "OPAQUE_NATIVE"},
|
||||
{"type": "agent_message", "content": [
|
||||
{"type": "encrypted_content", "encrypted_content": "OPAQUE_PEER"}]}):
|
||||
req = {"model": "gpt-6-astra", "input": [
|
||||
{"role": "user", "content": "KARAR-4827 " + "x" * 190000},
|
||||
{"type": "reasoning", "encrypted_content": "OPAQUE_NATIVE"},
|
||||
{"role": "user", "content": "Latest"}]}
|
||||
opaque, {"role": "user", "content": "Latest"}]}
|
||||
before = copy.deepcopy(req)
|
||||
result = self.service._prepare_transfer({"request": req, "source_model": "ide/test"})["request"]
|
||||
self.assertEqual(result["model"], "gpt-6-astra")
|
||||
self.assertIn("KARAR-4827", json.dumps(result))
|
||||
self.assertLessEqual(gc.tokens(result), gc.input_budget(req, 128000))
|
||||
self.assertEqual(result, before)
|
||||
self.assertEqual(req, before)
|
||||
self.assertTrue(self.server.gelen)
|
||||
self.assertTrue(all(b"OPAQUE_NATIVE" not in call["govde"] for call in self.server.gelen))
|
||||
self.assertEqual(self.server.gelen, [])
|
||||
|
||||
def test_external_compaction_uses_external_summary_not_subscription(self):
|
||||
req = {"model": "ide/test", "input": [{"role": "assistant", "content": "KARAR-4827"}, {"role": "user", "content": "KARAR-4827"}, {"type": "compaction_trigger"}]}
|
||||
@@ -281,30 +280,38 @@ class PreparationService(unittest.TestCase):
|
||||
|
||||
|
||||
class SocketMigration(fixtures.Ayirici):
|
||||
def test_opaque_peer_transfer_fails_as_http_400_before_sse_or_upstream(self):
|
||||
def test_opaque_peer_transfer_succeeds_without_ciphertext_or_transfer_error(self):
|
||||
req = {"model": "ide/agy-gemini-3.8-flash-high", "prompt_cache_key": "opaque-peer",
|
||||
"input": [{"type": "agent_message", "author": "/root", "recipient": "/root/peer",
|
||||
"content": [{"type": "input_text", "text": "Message Type: NEW_TASK\nPayload:\n"},
|
||||
{"type": "encrypted_content", "encrypted_content": "gAAAA_synthetic_opaque"}]}]}
|
||||
"input": [{"role": "user", "content": "USER_DECISION"},
|
||||
{"type": "agent_message", "author": "/root", "recipient": "/root/peer",
|
||||
"content": [{"type": "input_text", "text": "VISIBLE_PEER_HEADER"},
|
||||
{"type": "encrypted_content", "encrypted_content": "gAAAA_synthetic_opaque"}]},
|
||||
{"role": "assistant", "content": "VISIBLE_ANSWER"},
|
||||
{"type": "function_call", "id": "fc_1", "call_id": "c1", "name": "read", "arguments": "{}"},
|
||||
{"type": "function_call_output", "id": "fco_1", "call_id": "c1", "output": "VISIBLE_RESULT"}]}
|
||||
original = copy.deepcopy(req)
|
||||
raw = json.dumps(req).encode()
|
||||
# Identical retries must receive the same permanent error without a
|
||||
# preparation request, a stream heartbeat, or provider inference.
|
||||
for _ in range(2):
|
||||
status, body, headers = self.iste("POST", "/backend-api/codex/responses", raw)
|
||||
self.assertEqual(status, 400)
|
||||
self.assertEqual(headers["Content-Type"].split(";")[0], "application/json")
|
||||
self.assertNotIn("Transfer-Encoding", headers)
|
||||
error = json.loads(body)["error"]
|
||||
self.assertEqual(error["code"], "aiturk_transfer_failed")
|
||||
self.assertEqual(error["type"], "invalid_request_error")
|
||||
self.assertFalse(error["retryable"])
|
||||
self.assertIn("görev eksik gönderilmedi", error["message"])
|
||||
self.assertNotIn(b"response.failed", body)
|
||||
self.assertEqual(req, original)
|
||||
for model in (req["model"], "ide/another-provider"):
|
||||
req["model"] = model
|
||||
status, body, _ = self.iste("POST", "/backend-api/codex/responses", json.dumps(req).encode())
|
||||
self.assertEqual(status, 200)
|
||||
self.assertNotIn(b"aiturk_transfer_failed", body)
|
||||
sent = json.loads(self.kopru.gelen[-1]["govde"])
|
||||
wire = json.dumps(sent, ensure_ascii=False)
|
||||
self.assertNotIn("gAAAA_synthetic_opaque", wire)
|
||||
self.assertNotIn("encrypted_content", wire)
|
||||
self.assertIn(gc.PEER_OMITTED, wire)
|
||||
self.assertIn("VISIBLE_PEER_HEADER", wire)
|
||||
for item in original["input"][:1] + original["input"][2:]:
|
||||
self.assertIn(item, sent["input"])
|
||||
self.assertEqual(req["input"], original["input"])
|
||||
self.assertEqual(self.ozgun.gelen, [])
|
||||
self.assertEqual(self.kopru.gelen, [])
|
||||
self.assertEqual(bridge.TRANSFERS, {})
|
||||
# Returning uses the client's original transcript, including opaque peer
|
||||
# state. Projection never rewrites the persisted source or a native turn.
|
||||
req["model"] = "gpt-6-astra"
|
||||
raw = zstd(json.dumps(req).encode())
|
||||
status, _, _ = self.iste("POST", "/backend-api/codex/responses", raw, {"Content-Encoding": "zstd"})
|
||||
self.assertEqual(status, 200)
|
||||
self.assertEqual(self.ozgun.gelen[-1]["govde"], raw)
|
||||
|
||||
def test_native_opaque_peer_history_remains_byte_identical_on_owner_route(self):
|
||||
req = {"model": "gpt-6-astra", "input": [{"type": "agent_message", "author": "/root",
|
||||
|
||||
@@ -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 = '22'
|
||||
export const CODEX_BRIDGE_VERSION = '23'
|
||||
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'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "aiturk-ide",
|
||||
"productName": "AITURK IDE",
|
||||
"private": true,
|
||||
"version": "1.0.0-beta.44",
|
||||
"version": "1.0.0-beta.45",
|
||||
"description": "TurkServis resmi yapay zekâ geliştirme ortamı. Hermes Agent tabanlıdır.",
|
||||
"author": "AITURK / TurkServis",
|
||||
"repository": {
|
||||
|
||||
Generated
+1
-1
@@ -65,7 +65,7 @@
|
||||
},
|
||||
"apps/desktop": {
|
||||
"name": "aiturk-ide",
|
||||
"version": "1.0.0-beta.44",
|
||||
"version": "1.0.0-beta.45",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@assistant-ui/core": "0.2.23",
|
||||
|
||||
Reference in new issue
Block a user