From c0ac450da481fd2e58ff881c4945bca009b45298 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 14:48:15 +0900 Subject: [PATCH 01/11] test(ci): probe pinned request size enforcement Signed-off-by: Seongho Bae --- .../ci/contextual_orchestrator_review_sidecar.sh | 13 +++++++++++++ ...ntextual_orchestrator_review_sidecar_contract.py | 4 ++++ 2 files changed, 17 insertions(+) diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 023cedb386..cc74dbbd40 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -87,6 +87,19 @@ PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" -c \ 'from contextual_orchestrator.credentials import get_credential; from contextual_orchestrator.model_discovery import discover_all_models, free_discovered_models; from contextual_orchestrator.orchestrator import ModelClient, TaskOrchestrator, load_agents; from contextual_orchestrator.review_gateway import register_review_credentials; from contextual_orchestrator.server import SecurityConfig, serve' PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" -c \ 'from contextual_orchestrator.server import SecurityConfig; from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES; SecurityConfig(auth_token="contract", max_body_bytes=REVIEW_MAX_BODY_BYTES)' +PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" - <<'PY' +from contextual_orchestrator.server import RequestError, _request_body_size +from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES + +accepted_size = 64 * 1024 + 1 +assert _request_body_size({"content-length": str(accepted_size)}, REVIEW_MAX_BODY_BYTES) == accepted_size +try: + _request_body_size({"content-length": str(REVIEW_MAX_BODY_BYTES + 1)}, REVIEW_MAX_BODY_BYTES) +except RequestError as exc: + assert exc.code == 413 and exc.message == "request body exceeds configured limit" +else: + raise AssertionError("pinned server did not enforce the configured review body limit") +PY discovery_report="$ORCHESTRATOR_WORK/discovery-free.json" zdr_feed="$ORCHESTRATOR_WORK/openrouter-zdr-endpoints.json" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 6d8c7d66f6..efc3ff1bfd 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -294,6 +294,10 @@ def test_sidecar_validates_the_pinned_server_body_limit_constructor() -> None: """The exact vendored SHA must accept the review envelope keyword at boot.""" text = _read(SIDECAR) assert 'from contextual_orchestrator.server import SecurityConfig; from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES; SecurityConfig(auth_token="contract", max_body_bytes=REVIEW_MAX_BODY_BYTES)' in text + assert "from contextual_orchestrator.server import RequestError, _request_body_size" in text + assert "accepted_size = 64 * 1024 + 1" in text + assert "REVIEW_MAX_BODY_BYTES + 1" in text + assert 'exc.message == "request body exceeds configured limit"' in text def test_autofix_workflow_provisions_sidecar_with_all_five_secrets() -> None: From 548e1a064ef500f567238cd7a143f8885512b587 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 15:00:00 +0900 Subject: [PATCH 02/11] test(ci): assert HTTP status for body limit Signed-off-by: Seongho Bae --- scripts/ci/contextual_orchestrator_review_sidecar.sh | 2 +- tests/test_contextual_orchestrator_review_sidecar_contract.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index cc74dbbd40..a64dd2875f 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -96,7 +96,7 @@ assert _request_body_size({"content-length": str(accepted_size)}, REVIEW_MAX_BOD try: _request_body_size({"content-length": str(REVIEW_MAX_BODY_BYTES + 1)}, REVIEW_MAX_BODY_BYTES) except RequestError as exc: - assert exc.code == 413 and exc.message == "request body exceeds configured limit" + assert exc.status == 413 and exc.message == "request body exceeds configured limit" else: raise AssertionError("pinned server did not enforce the configured review body limit") PY diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index efc3ff1bfd..2107080c80 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -297,6 +297,7 @@ def test_sidecar_validates_the_pinned_server_body_limit_constructor() -> None: assert "from contextual_orchestrator.server import RequestError, _request_body_size" in text assert "accepted_size = 64 * 1024 + 1" in text assert "REVIEW_MAX_BODY_BYTES + 1" in text + assert "exc.status == 413" in text assert 'exc.message == "request body exceeds configured limit"' in text From eec8eb3b8d00958078884bc3e78ea1dd04ed0ef3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 15:08:10 +0900 Subject: [PATCH 03/11] docs(ci): record post-merge envelope evidence Signed-off-by: Seongho Bae --- docs/product-technical-gap-baseline.md | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 5a31b19147..ba5f19e89c 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -300,11 +300,13 @@ flowchart LR ## 2026-08-28 #1374 trusted-base runtime boundary -- Follow-up PR #1374 is open at head - `d3e7cee4b01219cb0a93f1a4249049de3bf05b4b`, based on current main - `8f84b661e468de451ba5c076dc938f342bf52d70`. Its launcher sets the bounded +- Follow-up PR #1374 merged at head + `3d7cf123ea7459b7f0082bb354280288866256db` with merge commit + `7c55295ff2dd863d983822d991e67ba037e8f186`; its launcher sets the bounded 8 MiB review envelope, and its sidecar boot check validates that keyword - against the exact pinned orchestrator SHA before discovery. + against the exact pinned orchestrator SHA before discovery. Its terminal + review decision was not an independent `APPROVED`, so this remains an + observed merge event rather than protected-main governance proof. - PR-target Strix run `33145070402` used trusted workflow source SHA `8f84b661e468de451ba5c076dc938f342bf52d70`, not the PR launcher. It reached the pinned sidecar and then failed three bounded attempts with HTTP 413 @@ -314,8 +316,14 @@ flowchart LR `orchestrator/free`, then skipped before the LLM call because the current head had no primary OpenCode approval. Required OpenCode run `33145070315` failed closed for the same missing current-head verdict. Therefore the - envelope fix still needs a normal governed merge followed by a post-merge - Strix runtime result; no protected completion is claimed here. + PR-target result was not an LLM verdict. +- Post-merge Strix run `33145807836` used trusted workflow source SHA + `7c55295ff2dd863d983822d991e67ba037e8f186`, reached + `openai/orchestrator/free`, and produced no HTTP 413 or + `request_too_large`. It failed closed after three bounded attempts because + the Strix Caido target was unavailable at `127.0.0.1:48080`, reported as + `STRIX_PROVIDER_UNAVAILABLE`; this proves the request-envelope fix on main, + but not a successful end-to-end vulnerability scan. ## 5. 실행 루프와 고객의 다음 행동 From e7a1ef07bb14df7f171b2d562fda93ed5c1d270f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 15:17:30 +0900 Subject: [PATCH 04/11] fix(ci): support portable token file metadata Signed-off-by: Seongho Bae --- .../ci/load_contextual_orchestrator_token.sh | 22 +++++++++++++++---- ...al_orchestrator_review_sidecar_contract.py | 7 ++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index 05eeeac0cb..9a17dd179f 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -8,6 +8,20 @@ _contextual_orchestrator_token_fail() { return 1 } +_contextual_orchestrator_stat() { + local format="$1" target="$2" value + + if value="$(stat -c "$format" -- "$target" 2>/dev/null)"; then + printf '%s\n' "$value" + return 0 + fi + if [ "$format" = "%a" ]; then + stat -f %OLp "$target" + return 0 + fi + stat -f "$format" "$target" +} + _contextual_orchestrator_load_token() { local token_file token_size @@ -18,10 +32,10 @@ _contextual_orchestrator_load_token() { if [ ! -f "$token_file" ] || [ -L "$token_file" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must name a regular, non-symlink file." || return 1 fi - if [ "$(stat -c %u -- "$token_file")" != "$(id -u)" ]; then + if [ "$(_contextual_orchestrator_stat %u "$token_file")" != "$(id -u)" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must be owned by the current runner user." || return 1 fi - if [ "$(stat -c %a -- "$token_file")" != "600" ]; then + if [ "$(_contextual_orchestrator_stat %a "$token_file")" != "600" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must have mode 600." || return 1 fi token_size="$(wc -c < "$token_file")" @@ -41,7 +55,7 @@ _contextual_orchestrator_load_token() { _contextual_orchestrator_load_token || { _contextual_orchestrator_status=$? - unset -f _contextual_orchestrator_load_token _contextual_orchestrator_token_fail + unset -f _contextual_orchestrator_load_token _contextual_orchestrator_stat _contextual_orchestrator_token_fail return "$_contextual_orchestrator_status" } -unset -f _contextual_orchestrator_load_token _contextual_orchestrator_token_fail +unset -f _contextual_orchestrator_load_token _contextual_orchestrator_stat _contextual_orchestrator_token_fail diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 2107080c80..78979de8ec 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -110,8 +110,11 @@ def test_token_loader_rehydrates_and_masks_bearer_inside_each_consumer_step() -> assert 'CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-' in text assert '[ ! -f "$token_file" ]' in text assert '[ -L "$token_file" ]' in text - assert 'stat -c %a -- "$token_file"' in text - assert 'stat -c %u -- "$token_file"' in text + assert '_contextual_orchestrator_stat()' in text + assert 'stat -c "$format" -- "$target"' in text + assert '[ "$format" = "%a" ]' in text + assert 'stat -f %OLp "$target"' in text + assert 'stat -f "$format" "$target"' in text assert "CONTEXTUAL_ORCHESTRATOR_TOKEN must not contain CR or LF" in text assert "printf '::add-mask::%s\\n' \"$CONTEXTUAL_ORCHESTRATOR_TOKEN\"" in text assert "export CONTEXTUAL_ORCHESTRATOR_TOKEN" in text From 5b78687d9dc6ad174024e5f049c3c053aa46cb28 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 15:28:32 +0900 Subject: [PATCH 05/11] fix(ci): probe review body limit through public server Replace the pinned sidecar preflight's private request parser probe with a public loopback HTTP check. Keep the bounded 8 MiB review envelope and document the black-box evidence. Signed-off-by: Seongho Bae --- ...ontextual-orchestrator-vendored-sidecar.md | 5 +- .../contextual_orchestrator_review_sidecar.sh | 46 +++++++++++++++---- ...al_orchestrator_review_sidecar_contract.py | 13 +++--- 3 files changed, 48 insertions(+), 16 deletions(-) diff --git a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md index 07f53f28a9..e3cf4bcf05 100644 --- a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md +++ b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md @@ -16,7 +16,10 @@ request with HTTP 413 (`request_too_large`). The vendored server's generic include tool schemas and repository context and therefore need a larger, still-bounded integration envelope. The review launcher now sets an explicit 8 MiB limit for this sidecar only; the library default remains unchanged for -other deployments. A 413 remains fail-closed if a request exceeds that bound. +other deployments. Its pinned-SHA preflight uses the public server builder and +loopback HTTP boundary to verify that an oversized request returns 413; it does +not depend on private server helpers or error-message text. A 413 remains +fail-closed if a request exceeds that bound. ## What changed diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index a64dd2875f..4d6e11b22d 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -85,20 +85,48 @@ python3 -m pip install --quiet --disable-pip-version-check --no-cache-dir \ -r "$requirements_lock" PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" -c \ 'from contextual_orchestrator.credentials import get_credential; from contextual_orchestrator.model_discovery import discover_all_models, free_discovered_models; from contextual_orchestrator.orchestrator import ModelClient, TaskOrchestrator, load_agents; from contextual_orchestrator.review_gateway import register_review_credentials; from contextual_orchestrator.server import SecurityConfig, serve' -PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" -c \ - 'from contextual_orchestrator.server import SecurityConfig; from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES; SecurityConfig(auth_token="contract", max_body_bytes=REVIEW_MAX_BODY_BYTES)' PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" - <<'PY' -from contextual_orchestrator.server import RequestError, _request_body_size +import http.client +import threading + +from contextual_orchestrator.orchestrator import ModelAgent, ModelClient, TaskOrchestrator +from contextual_orchestrator.server import SecurityConfig, build_server from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES accepted_size = 64 * 1024 + 1 -assert _request_body_size({"content-length": str(accepted_size)}, REVIEW_MAX_BODY_BYTES) == accepted_size +assert accepted_size < REVIEW_MAX_BODY_BYTES +orchestrator = TaskOrchestrator( + [ModelAgent(id="body_limit_probe", model="probe")], + client=ModelClient(max_output_tokens=1), +) +server = build_server( + orchestrator, + host="127.0.0.1", + port=0, + security=SecurityConfig(auth_token="contract", max_body_bytes=REVIEW_MAX_BODY_BYTES), +) +thread = threading.Thread(target=server.serve_forever, daemon=True) +thread.start() try: - _request_body_size({"content-length": str(REVIEW_MAX_BODY_BYTES + 1)}, REVIEW_MAX_BODY_BYTES) -except RequestError as exc: - assert exc.status == 413 and exc.message == "request body exceeds configured limit" -else: - raise AssertionError("pinned server did not enforce the configured review body limit") + connection = http.client.HTTPConnection("127.0.0.1", server.server_address[1], timeout=5) + connection.request( + "POST", + "/v1/chat/completions", + body=b"", + headers={ + "Authorization": "Bearer contract", + "Content-Type": "application/json", + "Content-Length": str(REVIEW_MAX_BODY_BYTES + 1), + }, + ) + response = connection.getresponse() + assert response.status == 413, response.status + response.read() + connection.close() +finally: + server.shutdown() + server.server_close() + thread.join(timeout=5) PY discovery_report="$ORCHESTRATOR_WORK/discovery-free.json" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 78979de8ec..ebd5f3e439 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -293,15 +293,16 @@ def test_launcher_sets_a_bounded_review_request_body_limit() -> None: assert "max_body_bytes=REVIEW_MAX_BODY_BYTES" in text -def test_sidecar_validates_the_pinned_server_body_limit_constructor() -> None: - """The exact vendored SHA must accept the review envelope keyword at boot.""" +def test_sidecar_probes_the_pinned_server_body_limit_at_http_boundary() -> None: + """The exact vendored SHA must enforce the review limit at its HTTP boundary.""" text = _read(SIDECAR) - assert 'from contextual_orchestrator.server import SecurityConfig; from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_BYTES; SecurityConfig(auth_token="contract", max_body_bytes=REVIEW_MAX_BODY_BYTES)' in text - assert "from contextual_orchestrator.server import RequestError, _request_body_size" in text + assert "from contextual_orchestrator.server import SecurityConfig, build_server" in text + assert '"POST",' in text + assert '"/v1/chat/completions",' in text assert "accepted_size = 64 * 1024 + 1" in text assert "REVIEW_MAX_BODY_BYTES + 1" in text - assert "exc.status == 413" in text - assert 'exc.message == "request body exceeds configured limit"' in text + assert "assert response.status == 413" in text + assert "_request_body_size" not in text def test_autofix_workflow_provisions_sidecar_with_all_five_secrets() -> None: From a38f931633522a6d49b4a86c93901716b018e313 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 15:55:35 +0900 Subject: [PATCH 06/11] fix(ci): accept bounded OpenAI tool descriptions Signed-off-by: Seongho Bae --- ...ontextual-orchestrator-vendored-sidecar.md | 14 ++++ docs/product-technical-gap-baseline.md | 17 +++++ .../contextual_orchestrator_review_sidecar.sh | 69 ++++++++++++++++++- .../ci/load_contextual_orchestrator_token.sh | 4 +- ...al_orchestrator_review_sidecar_contract.py | 18 ++++- 5 files changed, 115 insertions(+), 7 deletions(-) diff --git a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md index e3cf4bcf05..4393cabc0f 100644 --- a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md +++ b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md @@ -20,6 +20,20 @@ other deployments. Its pinned-SHA preflight uses the public server builder and loopback HTTP boundary to verify that an oversized request returns 413; it does not depend on private server helpers or error-message text. A 413 remains fail-closed if a request exceeds that bound. +This 8 MiB value is a local ingress safety bound, not an OpenAI limit. OpenAI's +official API reference describes a function-tool `description` as an optional +string without a 1024-character `maxLength`, and its published examples expose +model/token context limits rather than one universal JSON-body byte limit. +Endpoint-specific payload limits are separate: for example, the Files API +documents 512 MB per file and the Batch API 200 MB JSONL files. Therefore the +sidecar must measure representative Strix envelopes and keep provider/model +context failures distinct from its own HTTP framing failure. +The pin includes upstream `#887` (`2591b66`), which fixes the gateway's +incorrect 1024-character rejection. The same probe sends Strix-shaped function +tools with 1025-, 1026-, and 2000-character descriptions and verifies that +each reaches the provider payload byte-for-byte; arbitrary truncation is not +used. Provider/model-specific context limits remain provider errors, not a +reason for this gateway to rewrite the request. ## What changed diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index ba5f19e89c..d33875427b 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -325,6 +325,23 @@ flowchart LR `STRIX_PROVIDER_UNAVAILABLE`; this proves the request-envelope fix on main, but not a successful end-to-end vulnerability scan. +## 2026-08-28 OpenAI request-envelope specification check + +- OpenAI's official API reference models a function-tool `description` as an + optional string and does not publish a universal 1024-character field limit. + The official OpenAPI document also contains no `413` or + `request_too_large` response definition for the inference operations. The + `413 Content Too Large` observed above is therefore the vendored gateway's + HTTP framing response, not evidence of an OpenAI tool-description rule. +- OpenAI documents model-specific token/context limits and endpoint-specific + payload limits instead of one universal inference-body byte ceiling (for + example, Files permits 512 MB per file and Batch permits 200 MB JSONL files). + The sidecar's 8 MiB limit is consequently an explicitly local, bounded + ingress policy and is not claimed to be an OpenAI guarantee. Its pinned-SHA + probe now accepts a body of 65,609 bytes and preserves 1,025-, 1,026-, and + 2,000-character tool descriptions byte-for-byte; provider/model context + failures remain separate runtime evidence. + ## 5. 실행 루프와 고객의 다음 행동 각 hourly pass는 아래 순서를 유지한다. diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 4d6e11b22d..3f575cd831 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-c60ec889bdd1b8dd0b2be53e60d7b758a4ece6b7}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. @@ -87,6 +87,7 @@ PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" -c \ 'from contextual_orchestrator.credentials import get_credential; from contextual_orchestrator.model_discovery import discover_all_models, free_discovered_models; from contextual_orchestrator.orchestrator import ModelClient, TaskOrchestrator, load_agents; from contextual_orchestrator.review_gateway import register_review_credentials; from contextual_orchestrator.server import SecurityConfig, serve' PYTHONPATH="$ORCHESTRATOR_SOURCE:$ORG_REPO_ROOT" "$(command -v python3)" - <<'PY' import http.client +import json import threading from contextual_orchestrator.orchestrator import ModelAgent, ModelClient, TaskOrchestrator @@ -95,9 +96,22 @@ from scripts.ci.contextual_orchestrator_review_launcher import REVIEW_MAX_BODY_B accepted_size = 64 * 1024 + 1 assert accepted_size < REVIEW_MAX_BODY_BYTES + + +class CaptureClient(ModelClient): + def __init__(self): + super().__init__(max_output_tokens=1) + self.proxy_payloads = [] + + def proxy_send(self, agent, endpoint, payload): + self.proxy_payloads.append(json.loads(json.dumps(payload, ensure_ascii=False))) + return super().proxy_send(agent, endpoint, payload) + + +client = CaptureClient() orchestrator = TaskOrchestrator( - [ModelAgent(id="body_limit_probe", model="probe")], - client=ModelClient(max_output_tokens=1), + [ModelAgent(id="body_limit_probe", model="openai/gpt-5")], + client=client, ) server = build_server( orchestrator, @@ -123,6 +137,55 @@ try: assert response.status == 413, response.status response.read() connection.close() + + def post_payload(payload): + encoded = json.dumps(payload, ensure_ascii=False).encode("utf-8") + connection = http.client.HTTPConnection( + "127.0.0.1", server.server_address[1], timeout=5 + ) + try: + connection.request( + "POST", + "/v1/chat/completions", + body=encoded, + headers={ + "Authorization": "Bearer contract", + "Content-Type": "application/json", + "Content-Length": str(len(encoded)), + }, + ) + response = connection.getresponse() + result = json.loads(response.read().decode("utf-8")) + return response.status, result, len(encoded) + finally: + connection.close() + + large_status, large_body, encoded_size = post_payload({ + "model": "openai/gpt-5", + "messages": [{"role": "user", "content": "x" * accepted_size}], + }) + assert large_status == 200, large_body + assert encoded_size > accepted_size + + for description_length in (1025, 1026, 2000): + prefix = "preserve bytes – 🙂 " + description = prefix + ("x" * (description_length - len(prefix))) + status, body, _ = post_payload({ + "model": "openai/gpt-5", + "messages": [{"role": "user", "content": "probe"}], + "tools": [{ + "type": "function", + "function": { + "name": "scan_target", + "description": description, + "parameters": {"type": "object", "properties": {}}, + }, + }], + }) + assert status == 200, body + assert len(description) == description_length + forwarded = client.proxy_payloads[-1]["tools"][0]["function"]["description"] + assert forwarded.encode("utf-8") == description.encode("utf-8") finally: server.shutdown() server.server_close() diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index 9a17dd179f..2125dcffa2 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -16,7 +16,7 @@ _contextual_orchestrator_stat() { return 0 fi if [ "$format" = "%a" ]; then - stat -f %OLp "$target" + stat -f '%OMp %OLp' "$target" return 0 fi stat -f "$format" "$target" @@ -35,7 +35,7 @@ _contextual_orchestrator_load_token() { if [ "$(_contextual_orchestrator_stat %u "$token_file")" != "$(id -u)" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must be owned by the current runner user." || return 1 fi - if [ "$(_contextual_orchestrator_stat %a "$token_file")" != "600" ]; then + if [ "$(_contextual_orchestrator_stat %a "$token_file")" != "0 600" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must have mode 600." || return 1 fi token_size="$(wc -c < "$token_file")" diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index ebd5f3e439..bcd4740df0 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -35,7 +35,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "c60ec889bdd1b8dd0b2be53e60d7b758a4ece6b7" +ORCH_PIN_SHA = "8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f" def _read(path: Path) -> str: @@ -113,7 +113,7 @@ def test_token_loader_rehydrates_and_masks_bearer_inside_each_consumer_step() -> assert '_contextual_orchestrator_stat()' in text assert 'stat -c "$format" -- "$target"' in text assert '[ "$format" = "%a" ]' in text - assert 'stat -f %OLp "$target"' in text + assert "stat -f '%OMp %OLp' \"$target\"" in text assert 'stat -f "$format" "$target"' in text assert "CONTEXTUAL_ORCHESTRATOR_TOKEN must not contain CR or LF" in text assert "printf '::add-mask::%s\\n' \"$CONTEXTUAL_ORCHESTRATOR_TOKEN\"" in text @@ -169,6 +169,12 @@ def run(candidate: Path) -> subprocess.CompletedProcess[str]: assert wrong_mode.returncode != 0 assert "must have mode 600" in wrong_mode.stderr + for special_mode in (0o1600, 0o2600, 0o4600): + token_file.chmod(special_mode) + special_bits = run(token_file) + assert special_bits.returncode != 0 + assert "must have mode 600" in special_bits.stderr + token_file.chmod(0o600) symlink = tmp_path / "bearer.link" symlink.symlink_to(token_file) @@ -303,6 +309,14 @@ def test_sidecar_probes_the_pinned_server_body_limit_at_http_boundary() -> None: assert "REVIEW_MAX_BODY_BYTES + 1" in text assert "assert response.status == 413" in text assert "_request_body_size" not in text + assert "class CaptureClient(ModelClient):" in text + assert '"description": description' in text + assert "large_status" in text + assert "assert encoded_size > accepted_size" in text + assert "for description_length in (1025, 1026, 2000)" in text + assert "assert status == 200" in text + assert "proxy_payloads[-1]" in text + assert '"utf-8"' in text def test_autofix_workflow_provisions_sidecar_with_all_five_secrets() -> None: From 9d2d49d0fa752a5d6609e02dd81fbfaec789835b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 16:01:04 +0900 Subject: [PATCH 07/11] fix(ci): accept portable token permission output Signed-off-by: Seongho Bae --- scripts/ci/load_contextual_orchestrator_token.sh | 12 ++++++++---- ...ontextual_orchestrator_review_sidecar_contract.py | 6 +++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/scripts/ci/load_contextual_orchestrator_token.sh b/scripts/ci/load_contextual_orchestrator_token.sh index 2125dcffa2..7b3b1fbba1 100755 --- a/scripts/ci/load_contextual_orchestrator_token.sh +++ b/scripts/ci/load_contextual_orchestrator_token.sh @@ -23,7 +23,7 @@ _contextual_orchestrator_stat() { } _contextual_orchestrator_load_token() { - local token_file token_size + local token_file token_mode token_size token_file="${CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE:-}" if [ -z "$token_file" ]; then @@ -35,9 +35,13 @@ _contextual_orchestrator_load_token() { if [ "$(_contextual_orchestrator_stat %u "$token_file")" != "$(id -u)" ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must be owned by the current runner user." || return 1 fi - if [ "$(_contextual_orchestrator_stat %a "$token_file")" != "0 600" ]; then - _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must have mode 600." || return 1 - fi + token_mode="$(_contextual_orchestrator_stat %a "$token_file")" + case "$token_mode" in + 600|"0 600") ;; + *) + _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN_FILE must have mode 600." || return 1 + ;; + esac token_size="$(wc -c < "$token_file")" if [ "$token_size" -lt 1 ] || [ "$token_size" -gt 4096 ]; then _contextual_orchestrator_token_fail "CONTEXTUAL_ORCHESTRATOR_TOKEN must contain between 1 and 4096 bytes." || return 1 diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index bcd4740df0..997d79fe76 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -194,11 +194,11 @@ def test_token_loader_preserves_caller_locals_and_removes_helpers(tmp_path: Path token_path.write_text("synthetic-test-bearer", encoding="utf-8") token_path.chmod(0o600) command = ( - 'set -euo pipefail; token_file=caller-file; token_size=caller-size; ' + 'set -euo pipefail; token_file=caller-file; token_mode=caller-mode; token_size=caller-size; ' 'source "$TOKEN_LOADER"; ' 'declare -F _contextual_orchestrator_token_fail >/dev/null && exit 91; ' 'declare -F _contextual_orchestrator_load_token >/dev/null && exit 92; ' - 'printf "caller=%s:%s\\n" "$token_file" "$token_size"' + 'printf "caller=%s:%s:%s\\n" "$token_file" "$token_mode" "$token_size"' ) result = subprocess.run( ["bash", "-c", command], @@ -213,7 +213,7 @@ def test_token_loader_preserves_caller_locals_and_removes_helpers(tmp_path: Path ) assert result.returncode == 0, result.stderr - assert "caller=caller-file:caller-size" in result.stdout + assert "caller=caller-file:caller-mode:caller-size" in result.stdout def test_sidecar_scopes_private_umask_to_token_creation() -> None: From 4a25c46dc2fe046368f304a589885ebffb757dfc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 16:09:25 +0900 Subject: [PATCH 08/11] docs(ci): clarify multimodal request limits Signed-off-by: Seongho Bae --- ...ontextual-orchestrator-vendored-sidecar.md | 19 ++++++++++------- docs/product-technical-gap-baseline.md | 21 ++++++++++++------- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md index 4393cabc0f..15766abcd8 100644 --- a/docs/doctoring/contextual-orchestrator-vendored-sidecar.md +++ b/docs/doctoring/contextual-orchestrator-vendored-sidecar.md @@ -20,13 +20,18 @@ other deployments. Its pinned-SHA preflight uses the public server builder and loopback HTTP boundary to verify that an oversized request returns 413; it does not depend on private server helpers or error-message text. A 413 remains fail-closed if a request exceeds that bound. -This 8 MiB value is a local ingress safety bound, not an OpenAI limit. OpenAI's -official API reference describes a function-tool `description` as an optional -string without a 1024-character `maxLength`, and its published examples expose -model/token context limits rather than one universal JSON-body byte limit. -Endpoint-specific payload limits are separate: for example, the Files API -documents 512 MB per file and the Batch API 200 MB JSONL files. Therefore the -sidecar must measure representative Strix envelopes and keep provider/model +This 8 MiB value is a local ingress safety bound for this review sidecar, not a +general OpenAI multimodal limit. OpenAI's official images guide permits image +URLs, Base64 data URLs, and file IDs in ordinary model-input JSON and specifies +up to 512 MB total payload for an image-input request. The separate Files API +specifies up to 512 MB per uploaded file, while Batch has its own 200 MB JSONL +limit. A URL or file ID keeps the model-input JSON small; an inline Base64 image +can exceed this sidecar's 8 MiB review envelope. The sidecar therefore fails +that local boundary closed and must not claim general 512 MB multimodal +compatibility. Supporting that broader contract requires a separately governed +streaming/spooling path and provider capability checks; adding `/files` alone +would not handle an inline Base64 image in an ordinary JSON request. +The sidecar must measure representative Strix envelopes and keep provider/model context failures distinct from its own HTTP framing failure. The pin includes upstream `#887` (`2591b66`), which fixes the gateway's incorrect 1024-character rejection. The same probe sends Strix-shaped function diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index d33875427b..aa29370832 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -333,14 +333,19 @@ flowchart LR `request_too_large` response definition for the inference operations. The `413 Content Too Large` observed above is therefore the vendored gateway's HTTP framing response, not evidence of an OpenAI tool-description rule. -- OpenAI documents model-specific token/context limits and endpoint-specific - payload limits instead of one universal inference-body byte ceiling (for - example, Files permits 512 MB per file and Batch permits 200 MB JSONL files). - The sidecar's 8 MiB limit is consequently an explicitly local, bounded - ingress policy and is not claimed to be an OpenAI guarantee. Its pinned-SHA - probe now accepts a body of 65,609 bytes and preserves 1,025-, 1,026-, and - 2,000-character tool descriptions byte-for-byte; provider/model context - failures remain separate runtime evidence. +- OpenAI's current images-and-vision guide specifies up to 512 MB total payload + for an image-input request and accepts an image URL, Base64 data URL, or file + ID in ordinary model-input JSON. The Files API separately permits 512 MB per + uploaded file, and Batch separately permits 200 MB JSONL files. These are not + one universal limit for every JSON endpoint. The sidecar's 8 MiB limit is an + explicitly local, bounded policy for text/tool review envelopes and is not + claimed to provide general multimodal compatibility: a large inline Base64 + image can fail locally even though a URL or file ID keeps the JSON small. A + future general multimodal proxy needs a separately governed streaming/spooling + and provider-capability contract; `/files` alone does not cover inline image + data URLs. The pinned-SHA probe accepts a body of 65,609 bytes and preserves + 1,025-, 1,026-, and 2,000-character tool descriptions byte-for-byte; + provider/model context failures remain separate runtime evidence. ## 5. 실행 루프와 고객의 다음 행동 From 7053ef57f029abf2cce37b20962fc5939911ebd3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 16:25:08 +0900 Subject: [PATCH 09/11] docs(ci): record exact-head Strix backend failure Signed-off-by: Seongho Bae --- docs/product-technical-gap-baseline.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index aa29370832..ec6eafd6b5 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -346,6 +346,22 @@ flowchart LR data URLs. The pinned-SHA probe accepts a body of 65,609 bytes and preserves 1,025-, 1,026-, and 2,000-character tool descriptions byte-for-byte; provider/model context failures remain separate runtime evidence. +- PR #1379 exact head `4a25c46dc2fe046368f304a589885ebffb757dfc` + reached the pinned sidecar in Strix run `33150437853`; sidecar provisioning + and the request-envelope preflight passed, but all three scanner attempts + received HTTP 500 `internal_error` (request IDs + `7ef2a6bfd7494f80adbf9109b2f5dea2`, + `193276c218884651a3940dd9a30bcf97`, and + `ff529b84b101458eae03287d3e8df52d`). No 413 or vulnerability report was + emitted, so this is an incomplete provider/backend result rather than proof + of either request-size rejection or scan success. The pinned server currently + collapses otherwise-unhandled provider exceptions into that generic 500. + Contextual-orchestrator PR #904 is the separately governed candidate that + classifies upstream request-size rejection, retries eligible members of the + virtual `orchestrator/free` pool, and returns `request_too_large` only after + eligible-provider exhaustion. The sidecar pin must remain on protected main + until that change is merged and then be reverified by a fresh exact-head + Strix run. ## 5. 실행 루프와 고객의 다음 행동 From d2a9ca5f129d64b06efe94ccf2c9871c9788b558 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 28 Aug 2026 16:31:02 +0900 Subject: [PATCH 10/11] docs(ci): synchronize vendored orchestrator pin Signed-off-by: Seongho Bae --- .../adr/0003-contextual-orchestrator-vendored-free-zdr.md | 2 +- docs/product-technical-gap-baseline.md | 2 +- ...est_contextual_orchestrator_review_sidecar_contract.py | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 5f24f7029d..2d639281d9 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`c60ec889bdd1b8dd0b2be53e60d7b758a4ece6b7` today) into `RUNNER_TEMP`. The + (`8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index ec6eafd6b5..c1c3ad383f 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -226,7 +226,7 @@ flowchart LR OpenCode/Noema/Strix to the gateway; this snapshot lands the org-repo half. - `pr-review-autofix.yml` now provisions `scripts/ci/contextual_orchestrator_review_sidecar.sh` (pinned SHA - `c60ec889…`, same-process KV registration of `BYTEZ_API_KEY`, + `8d5924f8…`, same-process KV registration of `BYTEZ_API_KEY`, `NVIDIA_NIM_API_KEY`, `NVIDIA_NIM_API_KEY_SUB`, `OPENROUTER_API_KEY`, `OPENAI_API_KEY`, live auto model discovery, ZDR-prioritized free catalog), and the writer runs `--model contextual-orchestrator/orchestrator/free`. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index 997d79fe76..eb75f10701 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -25,6 +25,9 @@ OPENCODE_DISPATCH_WORKFLOW = _ORG_REPO_ROOT / ".github/workflows/opencode-review-dispatch.yml" STRIX_WORKFLOW = _ORG_REPO_ROOT / ".github/workflows/strix.yml" OPENCODE_CONFIG = _ORG_REPO_ROOT / "opencode.jsonc" +SIDECAR_ADR = ( + _ORG_REPO_ROOT / "docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md" +) FIVE_SECRETS = ( "BYTEZ_API_KEY", @@ -61,6 +64,11 @@ def test_sidecar_pins_the_vendored_orchestrator_revision() -> None: assert 'ORCHESTRATOR_HOST="127.0.0.1"' in text +def test_sidecar_adr_names_the_current_vendored_revision() -> None: + """The accepted decision record must not advertise a stale runtime SHA.""" + assert ORCH_PIN_SHA in _read(SIDECAR_ADR) + + def test_sidecar_requires_the_five_provider_secrets() -> None: """At least one of the five secrets must be present as bootstrap transport.""" text = _read(SIDECAR) From 67dbf0eb29df3b15e81cf3c7b71c2e7468f43487 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 00:09:48 +0900 Subject: [PATCH 11/11] fix(reviews): support OpenAI image request envelope Signed-off-by: Seongho Bae --- ...003-contextual-orchestrator-vendored-free-zdr.md | 13 ++++++++++++- .../ci/contextual_orchestrator_review_launcher.py | 8 ++++---- .../ci/contextual_orchestrator_review_sidecar.sh | 2 +- ...ntextual_orchestrator_review_sidecar_contract.py | 6 +++--- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md index 2d639281d9..033024812a 100644 --- a/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md +++ b/docs/adr/0003-contextual-orchestrator-vendored-free-zdr.md @@ -22,7 +22,7 @@ all five, and auto-optimize routing by cost. 1. **Vendoring, pinned**: `scripts/ci/contextual_orchestrator_review_sidecar.sh` clones `ContextualWisdomLab/contextual-orchestrator` at an exact SHA - (`8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f` today) into `RUNNER_TEMP`. The + (`b21645116b352967e50fc497b87eb745b9cc8c61` today) into `RUNNER_TEMP`. The source's `requirements.lock` is installed with `--require-hashes` and `--no-deps`, so dependency resolution cannot silently move the reviewed runtime. @@ -67,6 +67,13 @@ all five, and auto-optimize routing by cost. 5. **Evidence**: the sidecar writes a discovery report, the policy report (pool, counts, ZDR sources, feed-used flag, selected routes), and exports `CONTEXTUAL_ORCHESTRATOR_EVIDENCE`; these are auditable per run. +6. **Review request envelope**: the library keeps its generic 64 KiB default, + while this loopback, bearer-authenticated, per-job sidecar configures a + 512 MiB ceiling so inline image inputs can reach routing. This follows the + OpenAI image-input limit of 512 MB total payload per request; it is not + treated as a universal JSON default or as the Files API's separate 512 MB + per-file limit. The sidecar startup probe verifies the configured HTTP + boundary before any review model runs. ## Consequences @@ -91,6 +98,10 @@ all five, and auto-optimize routing by cost. - OpenRouter. (2026, August). *Provider logging: Data retention & logging* [Documentation]. https://openrouter.ai/docs/guides/privacy/provider-logging - OpenRouter. (n.d.). *List all models and their properties* API reference; the per-model data-retention metadata (`data_retention: crichton | none`) and the ZDR endpoint feed `https://openrouter.ai/api/v1/endpoints/zdr` are consumed at runtime. - ContextualWisdomLab/contextual-orchestrator. (2026, August 18). *AGENTS.md*, section “Policy change” — org migration of OpenCode/Noema/Strix to the gateway with the five KV credentials and auto-discovery. +- OpenAI. (n.d.). *Images and vision: Image input requirements*. + https://developers.openai.com/api/docs/guides/images-vision +- OpenAI. (n.d.). *Create file* [API reference]. + https://developers.openai.com/api/reference/resources/files/methods/create - **Private-target boundary (2026-08-27):** Noema resolves target visibility with the selected repository-scoped reviewer token. Private/internal repositories diff --git a/scripts/ci/contextual_orchestrator_review_launcher.py b/scripts/ci/contextual_orchestrator_review_launcher.py index 83d8109e9e..de2cf6f766 100644 --- a/scripts/ci/contextual_orchestrator_review_launcher.py +++ b/scripts/ci/contextual_orchestrator_review_launcher.py @@ -27,10 +27,10 @@ from pathlib import Path -# The vendored server's generic 64 KiB default is intentionally conservative, -# but Strix and Noema send tool schemas plus repository context in one request. -# Keep the review-specific envelope bounded without weakening the library default. -REVIEW_MAX_BODY_BYTES = 8 * 1024 * 1024 +# The vendored server's generic 64 KiB default is intentionally conservative. +# This loopback, bearer-authenticated review sidecar accepts OpenAI's image-input +# request ceiling so repository context can include inline image inputs. +REVIEW_MAX_BODY_BYTES = 512 * 1024 * 1024 def _free_report_rows(discovered: list[object]) -> list[dict[str, object]]: diff --git a/scripts/ci/contextual_orchestrator_review_sidecar.sh b/scripts/ci/contextual_orchestrator_review_sidecar.sh index 3f575cd831..7b24fed787 100755 --- a/scripts/ci/contextual_orchestrator_review_sidecar.sh +++ b/scripts/ci/contextual_orchestrator_review_sidecar.sh @@ -14,7 +14,7 @@ # (fail-closed zero-cost) pool. set -euo pipefail -ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f}" +ORCHESTRATOR_PIN_SHA="${ORCHESTRATOR_PIN_SHA:-b21645116b352967e50fc497b87eb745b9cc8c61}" ORCHESTRATOR_GIT_URL="${ORCHESTRATOR_GIT_URL:-https://github.com/ContextualWisdomLab/contextual-orchestrator.git}" # The Strix gate and Noema SSRF guard accept this one process-local origin. # Keep it fixed so an environment override cannot create an unvalidated sidecar. diff --git a/tests/test_contextual_orchestrator_review_sidecar_contract.py b/tests/test_contextual_orchestrator_review_sidecar_contract.py index eb75f10701..fbd01008c6 100644 --- a/tests/test_contextual_orchestrator_review_sidecar_contract.py +++ b/tests/test_contextual_orchestrator_review_sidecar_contract.py @@ -38,7 +38,7 @@ ) GATEWAY_MODEL = "contextual-orchestrator/orchestrator/free" -ORCH_PIN_SHA = "8d5924f8f7582ece18a6f43d6a5fffcb6a0a9c9f" +ORCH_PIN_SHA = "b21645116b352967e50fc497b87eb745b9cc8c61" def _read(path: Path) -> str: @@ -301,9 +301,9 @@ def test_launcher_requires_gateway_token_and_a_provider_credential() -> None: def test_launcher_sets_a_bounded_review_request_body_limit() -> None: - """Large review envelopes fit without changing the library's generic default.""" + """Review images fit without changing the library's generic default.""" text = _read(LAUNCHER) - assert "REVIEW_MAX_BODY_BYTES = 8 * 1024 * 1024" in text + assert "REVIEW_MAX_BODY_BYTES = 512 * 1024 * 1024" in text assert "max_body_bytes=REVIEW_MAX_BODY_BYTES" in text