Skip to content

fix(gateway): expose agent exit metadata to hooks - #2

Closed
ComBba wants to merge 3 commits into
mainfrom
codex/rel-01-fork-owned
Closed

fix(gateway): expose agent exit metadata to hooks#2
ComBba wants to merge 3 commits into
mainfrom
codex/rel-01-fork-owned

Conversation

@ComBba

@ComBba ComBba commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Summary

Expose trustworthy turn-exit metadata in the Gateway agent:end hook for the
owned Hermes fork.

  • Add turn_exit_reason and non-negative api_call_count.
  • Preserve explicit agent finalizer reasons.
  • Distinguish user interruption from Gateway timeout, disconnect, shutdown,
    restart, unclassified interrupt, runtime-resolution failure, and proxy exit
    classes.
  • Preserve pre-existing proxy downstream control flow while still reporting
    partial-stream evidence through turn_exit_reason.
  • Cover stale proxy generations without collapsing them into unknown.

Root cause

The Gateway emitted only the response text to agent:end. Finalizer metadata
was available in the agent result but did not reach the hook artifact and
conductor. Early Gateway and proxy exits also lacked a stable termination
classification.

Scope

Validation

  • Focused/unit/integration/adversarial suite: 11 files, 379 passed, 0 failed
  • py_compile: pass
  • Ruff 0.15.22: pass
  • Windows footgun diff scan: 4 Python files, 0 findings
  • git diff --check: pass
  • ASCII guard: changed EN and zh-Hans hook docs, 0 errors
  • Claude Code Opus 5 final exact-patch review: NO ACTIONABLE FINDINGS
  • Local worktree and fork remote read-back: exact and clean

Governance and deployment

This PR targets only ComBba/hermes-agent. No upstream Issue or PR is part of
this workflow. Merge, live deployment, Gateway restart, configuration changes,
and production writes remain outside this Draft PR and require separate
exact-SHA approval.

Rollback

Before merge, close the PR or revert the single candidate commit. Before any
approved deployment, preserve the live checkout SHA, Gateway PID, and hashes of
the deployed hook/conductor files. A deployment rollback must restore that
pre-state and prove PID, Discord reconnection, and hook/conductor read-back.

Summary by CodeRabbit

  • 새로운 기능

    • agent:end 이벤트에 에이전트 종료 사유(turn_exit_reason)와 API 호출 횟수(api_call_count)가 추가됩니다.
    • 사용자 중단, 시간 초과, 연결 오류 등 종료 상황이 일관된 사유로 분류됩니다.
    • 비정상 입력에도 종료 메타데이터가 안전하게 정규화됩니다.
  • 문서

    • 새 이벤트 필드와 종료 사유 분류 규칙을 관련 문서에 반영했습니다.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@ComBba, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ae438ff8-ff63-4cdc-bc75-8df2ad2b2bfd

📥 Commits

Reviewing files that changed from the base of the PR and between 7aa5bef and 2fc3cb5.

📒 Files selected for processing (7)
  • agent/codex_runtime.py
  • gateway/hooks.py
  • gateway/run.py
  • tests/agent/test_codex_app_server_persist.py
  • tests/gateway/test_agent_end_hook_metadata.py
  • website/docs/user-guide/features/hooks.md
  • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/hooks.md
📝 Walkthrough

Walkthrough

Gateway와 Proxy 실행 결과에 turn_exit_reasonapi_call_count가 추가되었습니다. 중단·오류·타임아웃·부분 응답을 정규화하고, agent:end 훅과 관련 테스트·문서에 반영했습니다.

Changes

에이전트 종료 메타데이터

Layer / File(s) Summary
종료 사유 정규화와 훅 메타데이터
gateway/hooks.py, gateway/run.py, tests/gateway/test_agent_end_hook_metadata.py
중단 메시지와 종료 사유를 정규화하고, agent:end에 200자 이내 turn_exit_reason과 0 이상 api_call_count를 안전하게 기록합니다. 런타임 실패와 비활동 타임아웃도 검증합니다.
Proxy 종료 상태 전파
gateway/run.py, tests/gateway/test_proxy_mode.py
Proxy 의존성·설정·HTTP·연결·스트림·세대 불일치 상태를 구분된 종료 사유로 반환하고, 결과 전파 및 SSE 테스트를 갱신합니다.
훅 메타데이터 문서화
website/docs/user-guide/features/hooks.md, website/i18n/zh-Hans/.../hooks.md
agent:end 필드와 종료 사유 정규화·확장 규칙, 훅 호출 우선순위를 문서화합니다.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GatewayRunner
  participant Agent
  participant AgentEndHook
  GatewayRunner->>Agent: 에이전트 실행 및 종료 정보 수집
  Agent->>GatewayRunner: 결과, 중단 정보, API 호출 횟수 반환
  GatewayRunner->>AgentEndHook: 정규화된 turn_exit_reason과 api_call_count 전달
Loading

Suggested reviewers: teknium1

Poem

토끼가 훅의 문을 톡톡 두드려
종료 사유를 가지런히 담고,
호출 횟수도 세어 보았네.
프록시 바람이 불어도 흔들림 없이,
새로운 메타데이터가 깡충 뛰네! 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.07% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Gateway의 agent:end 훅에 종료 메타데이터를 노출하는 मुख्य 변경을 정확히 요약합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/rel-01-fork-owned

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ComBba ComBba closed this Jul 30, 2026
@ComBba ComBba reopened this Jul 30, 2026
@ComBba
ComBba marked this pull request as ready for review July 30, 2026 02:50
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7aa5bef627

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread gateway/run.py
Comment thread gateway/run.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
tests/gateway/test_agent_end_hook_metadata.py (1)

122-126: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

agent:end 컨텍스트 추출 로직을 헬퍼로 묶는 편이 좋습니다.

동일한 리스트 컴프리헨션이 이 파일에서 9회 반복됩니다. 또한 call.args[0] 인덱싱은 프로덕션 호출이 키워드 인자로 바뀌면 IndexError로 실패하므로, 헬퍼에서 call.args/call.kwargs를 함께 다루면 더 견고해집니다.

♻️ 제안 리팩터
def _agent_end_contexts(runner):
    contexts = []
    for call in runner.hooks.emit.await_args_list:
        event = call.args[0] if call.args else call.kwargs.get("event_type")
        if event != "agent:end":
            continue
        contexts.append(
            call.args[1] if len(call.args) > 1 else call.kwargs.get("context")
        )
    return contexts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gateway/test_agent_end_hook_metadata.py` around lines 122 - 126,
Extract the repeated agent:end context list comprehension into a shared
_agent_end_contexts(runner) test helper and replace all nine occurrences with
it. Make the helper read the event and context from positional arguments when
present, otherwise fall back to the event_type and context keyword arguments,
while preserving the existing filtering and ordering.
tests/gateway/test_proxy_mode.py (3)

590-596: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

인접 bytes 리터럴이 하나의 청크로 결합됩니다.

콤마가 없어 두 줄이 단일 bytes로 이어집니다. 동작에는 문제가 없지만 위쪽 test_proxy_stale_generation_returns_empty_result(콤마로 2개 청크)와 형태가 달라 의도가 모호합니다. 다중 청크를 의도했다면 콤마를 추가해 주세요.

♻️ 제안
             sse_chunks=[
-                b'data: {"choices":[{"delta":{"content":"stale"}}]}\n\n'
-                b"data: [DONE]\n\n"
+                b'data: {"choices":[{"delta":{"content":"stale"}}]}\n\n',
+                b"data: [DONE]\n\n",
             ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gateway/test_proxy_mode.py` around lines 590 - 596, Update the
sse_chunks list in the _FakeSSEResponse setup to add a comma between the two
adjacent bytes literals, ensuring the stale content and [DONE] events are
represented as separate chunks like
test_proxy_stale_generation_returns_empty_result.

405-410: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

SSE 버퍼 캡 위반이 gateway_proxy_connection_error와 같은 사유로 뭉쳐집니다.

버퍼 초과는 업스트림 프로토콜 위반(줄 경계 없는 무한 스트림)이고 실제 연결 실패와 원인·대응이 다릅니다. 이 PR이 사유 어휘를 세분화하는 목적이라면 gateway_proxy_protocol_error 같은 별도 사유가 관측성에 유리합니다. 프로덕션 분류 변경이 필요하므로 후속 작업으로 남겨도 무방합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gateway/test_proxy_mode.py` around lines 405 - 410, Separate SSE
buffer-cap violations from connection failures in the proxy handling flow:
classify buffer overflow as gateway_proxy_protocol_error instead of
gateway_proxy_connection_error, while preserving the existing reason for genuine
connection failures. Update the assertions for result["turn_exit_reason"]
accordingly.

599-603: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

side_effect=[True, False]_is_session_run_current 호출 횟수에 취약합니다.

구현이 세대 확인을 3회 이상 호출하도록 바뀌면(예: 스트림 컨슈머 활성화 경로 추가) StopIteration으로 테스트가 깨져 원인 파악이 어렵습니다. "첫 호출만 현재, 이후는 stale" 의미를 함수로 표현하면 호출 횟수와 무관하게 동작합니다.

♻️ 제안 리팩터
+        calls = {"n": 0}
+
+        def _current_then_stale(*_args, **_kwargs):
+            calls["n"] += 1
+            return calls["n"] == 1
+
         with patch.object(
             runner,
             "_is_session_run_current",
-            side_effect=[True, False],
+            side_effect=_current_then_stale,
         ):
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/gateway/test_proxy_mode.py` around lines 599 - 603, 테스트의
`_is_session_run_current` 패치가 고정된 호출 횟수에 의존하고 있습니다. `side_effect=[True, False]`
대신 호출 상태를 추적하는 함수형 side effect를 사용해 첫 호출에는 현재 상태를 반환하고 이후 모든 호출에는 stale 상태를
반환하도록 수정하세요. 이렇게 `_is_session_run_current` 호출 횟수가 늘어나도 테스트가 동일한 의미를 유지하게 하세요.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@gateway/run.py`:
- Line 22042: Update the exception path returning
"gateway_agent_runtime_resolution_failed" to include "failed": True in the same
result object. Keep the existing turn_exit_reason and provider authentication
error details unchanged so _handle_message_with_agent recognizes this as an
early agent failure and avoids persisting the assistant error text.

In `@website/docs/user-guide/features/hooks.md`:
- Line 95: Update the finalizer-reason documentation to distinguish preserved
reason semantics from normalized delivery text: in
website/docs/user-guide/features/hooks.md lines 95-95, replace “pass through”
with wording that states the reason is preserved while the delivered string is
normalized; make the corresponding change from “原样传递” to “规范化后保留” in
website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/hooks.md
lines 91-92, keeping both documents consistent with one-line folding and
200-character truncation.

---

Nitpick comments:
In `@tests/gateway/test_agent_end_hook_metadata.py`:
- Around line 122-126: Extract the repeated agent:end context list comprehension
into a shared _agent_end_contexts(runner) test helper and replace all nine
occurrences with it. Make the helper read the event and context from positional
arguments when present, otherwise fall back to the event_type and context
keyword arguments, while preserving the existing filtering and ordering.

In `@tests/gateway/test_proxy_mode.py`:
- Around line 590-596: Update the sse_chunks list in the _FakeSSEResponse setup
to add a comma between the two adjacent bytes literals, ensuring the stale
content and [DONE] events are represented as separate chunks like
test_proxy_stale_generation_returns_empty_result.
- Around line 405-410: Separate SSE buffer-cap violations from connection
failures in the proxy handling flow: classify buffer overflow as
gateway_proxy_protocol_error instead of gateway_proxy_connection_error, while
preserving the existing reason for genuine connection failures. Update the
assertions for result["turn_exit_reason"] accordingly.
- Around line 599-603: 테스트의 `_is_session_run_current` 패치가 고정된 호출 횟수에 의존하고 있습니다.
`side_effect=[True, False]` 대신 호출 상태를 추적하는 함수형 side effect를 사용해 첫 호출에는 현재 상태를
반환하고 이후 모든 호출에는 stale 상태를 반환하도록 수정하세요. 이렇게 `_is_session_run_current` 호출 횟수가 늘어나도
테스트가 동일한 의미를 유지하게 하세요.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 986a765c-cbe8-4ddb-8942-ea28635c10ae

📥 Commits

Reviewing files that changed from the base of the PR and between 158e9a9 and 7aa5bef.

📒 Files selected for processing (6)
  • gateway/hooks.py
  • gateway/run.py
  • tests/gateway/test_agent_end_hook_metadata.py
  • tests/gateway/test_proxy_mode.py
  • website/docs/user-guide/features/hooks.md
  • website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/hooks.md
📜 Review details
🧰 Additional context used
🪛 ast-grep (0.45.0)
tests/gateway/test_proxy_mode.py

[warning] 235-235: Do not make http calls without encryption
Context: "http://host:8642"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)


[warning] 415-415: Do not make http calls without encryption
Context: "http://host:8642"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)


[warning] 446-446: Do not make http calls without encryption
Context: "http://host:8642"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)


[warning] 584-584: Do not make http calls without encryption
Context: "http://host:8642"
Note: [CWE-319] Cleartext Transmission of Sensitive Information.

(requests-http)

🔇 Additional comments (21)
website/docs/user-guide/features/hooks.md (1)

83-93: LGTM!

Also applies to: 96-107

website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/user-guide/features/hooks.md (1)

83-89: LGTM!

Also applies to: 94-100, 1347-1347

gateway/run.py (7)

2656-2749: 정규화 헬퍼 및 _gateway_turn_exit_reason 분류 로직 — 테스트 케이스와 정확히 일치함

_normalize_turn_exit_reason, _is_generic_agent_interrupt_exit_reason, _gateway_turn_exit_reason의 우선순위 로직(명시적 사유 우선 → 제네릭 interrupt* 사유는 인터럽트 마커로 재분류 → 폴백)을 test_agent_end_hook_metadata.py의 세 가지 케이스(가드레일 정지 사유 보존, Stop requestedinterrupted_by_user, 제어 문자 스트리핑)로 직접 추적 검증했고 모두 일치합니다.


14724-14738: LGTM!


20396-20397: LGTM!

Also applies to: 20407-20408


20539-20539: 프록시 스트리밍 경로의 turn_exit_reason 파생 로직 확인 완료

proxy_partial은 예외 발생 시점에 이미 full_response가 비어있지 않은 경우에만 True로 설정되고, 최종 반환부의 turn_exit_reason 삼항 분기(partial/complete/empty)는 이 불변조건과 일치합니다. "partial": False를 항상 반환하는 것은 주석에 명시된 대로 기존 다운스트림 제어 흐름을 보존하기 위한 의도적 설계이며, full_response가 항상 채워지므로("(No response from remote agent)" 폴백 포함) _normalize_empty_agent_response 등 하위 소비자에 영향이 없음을 확인했습니다.

Also applies to: 20561-20562, 20582-20584, 20626-20630, 20656-20657, 20675-20687


23182-23182: LGTM!

Also applies to: 23310-23313


23624-23624: LGTM!


23812-23812: LGTM!

gateway/hooks.py (1)

33-43: LGTM!

tests/gateway/test_agent_end_hook_metadata.py (6)

18-76: LGTM!


79-161: LGTM!


164-225: LGTM!


258-350: LGTM!


353-476: LGTM!


479-661: LGTM!

tests/gateway/test_proxy_mode.py (5)

43-59: LGTM!


234-268: LGTM!


346-348: LGTM!

Also applies to: 379-381


412-467: LGTM!


537-539: LGTM!

Comment thread gateway/run.py
Comment thread website/docs/user-guide/features/hooks.md Outdated
@ComBba

ComBba commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Review for the governed merge — conditional, one change needed

This pull request is being reviewed as a compensating control, not as a formality. The governance sandbox cannot meaningfully validate this repository: its toolchain image has no pip and no network, while production code imports 32 exact-pinned third-party packages. Measured directly — import gateway.run fails with ModuleNotFoundError: No module named 'yaml', and python3 -m unittest discover -s tests runs 2266 tests of which 47 pass. So automated validation here can only confirm that the Python parses. Human review stands in for the rest, and it has to actually do that job.

Finding — the stated problem remains in its most severe case

agent:end is emitted at two places on this head (2fc3cb5af4f5b58c7885628ac07c70534430ab1c): line 14578 in the stale-generation branch this pull request adds, and line 14777 on the normal path. Both sit inside the ~650-line try in _handle_message_with_agent.

The catch-all at line 15169 does not:

_handle_message_with_agent catch-all except: line 15169
  contains "agent:end": False
  contains hooks.emit:  False

agent:start has already fired by then. If anything inside that try raises — session-compaction persistence, run_conversation() itself, a provider client failure before run_sync is entered — the block stops the typing indicator, persists the user message, returns an error string, and emits nothing. The hook learns the turn began and never learns it ended.

That is the exact phenomenon this pull request describes as a silent abnormal exit, still present in the most abnormal case there is: a crash. It also breaks the agent:start/agent:end pairing invariant this pull request's own tests establish.

The 815-line test file uses runner._run_agent = AsyncMock(return_value=...) throughout and never once uses side_effect=Exception(...), so nothing covers this path.

Fix: emit agent:end from the catch-all with turn_exit_reason="gateway_unhandled_exception" and api_call_count=0, following the same pattern the stale branch already uses, and add a test that makes _run_agent raise.

Secondary observations

Documentation is inaccurate about proxy mode. The new text says proxy mode reports 0, but the successful proxy path returns "api_calls": 1 (gateway/run.py:20712, unchanged here). 0 appears only on error and edge paths — confirmed against tests/test_proxy_mode.py:185 versus lines 305/361/468. Since this field will feed alert classification, the generalization could cause a normally-completed proxy turn and an error-truncated one to be read the same way.

A raw exception message can now reach the hook. agent/conversation_loop.py:6990 and :6993 build local_processing_error({error_msg[:80]}) and error_near_max_iterations({error_msg[:80]}), where error_msg embeds str(e). Those lines are unchanged and outside this diff, but before this pull request that value went nowhere. Now it flows through gateway_turn_exit_reason() into the agent:end payload, normalized and truncated to 200 characters but otherwise intact. That sits uneasily against this repository's contract that hook artifacts never record conversation content. Worth deciding deliberately — the new code in agent/codex_runtime.py uses fixed literals and has no such exposure.

Stale reclassification is asymmetric. Only gateway_proxy_response_complete is relabelled to gateway_proxy_stale_generation; gateway_proxy_partial_response and gateway_proxy_empty_response are not, and neither is tested. Consistent with the documented intent that more specific abnormal reasons survive, but the code alone cannot distinguish design from omission.

What was verified, and how

The twelve deleted lines are a refactor of _CONTROL_INTERRUPT_MESSAGES into a generator-built frozenset plus a shared _normalize_interrupt_message() helper. Both versions were reimplemented and compared across fifteen inputs — None, empty, whitespace, 0, False, case and spacing variants, arbitrary objects, lists — with identical results throughout.

The new payload keys are purely additive; the eight existing keys are untouched, so current consumers are unaffected.

The classification helpers were extracted from the diff and run against twenty-one scenarios covering normal completion, five system interrupt markers, unclassified interrupts, user correction text, generic-interrupt override, an unstringable reason, an exploding count, ANSI and NUL control characters, and negative counts. All matched expectations. User correction text returns the fixed literal interrupted_by_user and never the message itself.

Every new function is pure and reads only from the per-call agent_result; no self. state, no module-level mutable, so nothing crosses between concurrent sessions. api_call_count is bounded below by max(0, int(...)) and above in practice by max_iterations; turn_exit_reason is capped at 200 characters; both fall back safely inside their own try/except.

Not covered

gateway.run could not be imported or executed in this environment, so the suite this pull request reports as passing was not re-run here. The verification above reproduces the changed functions as standalone code rather than exercising them in place. Runtime behaviour, live Discord reconnection, real concurrent sessions, and the consuming hook in the operations repository were all out of reach.

@ComBba

ComBba commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #3.

The blocker was not the review finding — it was the base. This PR targets 158e9a99, which trails NousResearch upstream by 809 commits and carries no commits of its own. The running gateway checkout is at 98105f31, 538 commits ahead of that base.

git diff 158e9a99..98105f31 → 2,893 files changed, +115,001 / −431,282

So merging this PR and deploying the result would have rolled the running gateway back 538 commits. Issue NousResearch#15's remaining acceptance criteria require proving a deployed SHA and a live hook artifact — which that deploy could not honestly satisfy.

#3 re-derives this change on 98105f31 instead, so the deploy becomes a minimal forward delta. Because the head SHA changes either way, the catch-all fix requested in #2 (comment) is included there rather than deferred: it costs nothing extra inside a rebuild that was already required.

All five test functions this PR newly introduced in tests/gateway/test_proxy_mode.py are carried over. Assertions this PR added to tests that upstream has since deleted are not revived.

Leaving this open pending the merge decision on #3.

@ComBba

ComBba commented Aug 7, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #3, merged as `34845717347e4b696667a14ff20f6a4c1f1c05f8` and deployed. That branch carries a strictly more evolved version of this change — _gateway_turn_exit_reason, 200-character truncation, a stale flag, and codex_app_server coverage.

@ComBba ComBba closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant