Repository navigation
refactor(state): normalize full_state unwrapping in RemoteState once - #248
Conversation
Endpoint audit❌ 7 off-contract call(s) — not on the agent-server · classifiers: cloud
❌ Not on agent-server (gated, 7)⛔ (no known backend) — served by no backend we can see (6)
|
Seven accessors (getExecutionStatus, getConfirmationPolicy, getAgent, getWorkspace, getPersistenceDir, getActivatedKnowledgeSkills, modelDump) each re-ran a per-accessor unwrap of the API's `full_state` wrapper. Make getConversationInfo() the single unwrap point and drop the per-accessor unwrapping. Crucially, getConversationInfo() previously normalized only the fresh REST fetch, not the cache-hit return — and the cache can be populated by state-update events (createStateUpdateCallback -> updateStateFromEvent) that still carry the `full_state` wrapper. So the normalization now runs on the cache-hit path too; otherwise accessors would throw "execution_status missing" against an event-populated cache. Purely internal (the helper is private); public behavior is unchanged. Adds RemoteState unit coverage for flat and full_state-wrapped REST payloads plus a regression test for the event-populated wrapped cache (the case the integration tests exercise). Closes #109
37c94bd to
9e6ce98
Compare
|
@OpenHands Review this PR. If you find is correct and there are no problem. Accept it. |
|
I'm on it! VascoSch92 can track my progress at all-hands.dev |
|
🟢 Good taste I reviewed the PR diff, linked issue OpenHands/software-agent-sdk#4733, and the I verified the targeted unit test locally: Result: passed, 5/5 tests. GitHub also reports all PR checks successful, including CI and integration tests. [RISK ASSESSMENT]
This is a private internal refactor with focused regression coverage and no public API behavior change. VERDICT: ✅ Worth merging I attempted to submit an approving review, but GitHub rejected it with: This comment was created by an AI agent (OpenHands) on behalf of the user. |
|
@OpenHands /codereview-roasted and post ypur feedback on pr with gh api |
|
I'm on it! enyst can track my progress at all-hands.dev |
enyst
left a comment
There was a problem hiding this comment.
🟢 Good taste
This is the right cut: normalize the wire-shape at the state boundary, not seven times in seven accessors. The important part is that getConversationInfo() now handles both fresh REST responses and cache hits, which preserves the event-populated cache path that made the old per-accessor unwrap load-bearing.
I reviewed the PR diff, src/conversation/remote-state.ts, the new src/__tests__/remote-state.test.ts, and linked issue #109. I also ran the targeted regression suite locally:
npm test -- --runTestsByPath src/__tests__/remote-state.test.ts --runInBand
Result: passed, 5/5 tests. npm ci also completed the TypeScript build successfully during install.
No blocking issues found. The regression test covers the exact failure mode: a cached state populated from a state-update event carrying a full_state wrapper must still produce flat accessor results without hitting the network.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
This is a private-helper refactor with preserved public behavior, narrower duplication, and focused regression coverage around the cache/event path. The main compatibility risk would have been leaving wrapped cache hits unnormalized; this PR explicitly addresses and tests that path.
VERDICT:
✅ Worth merging: Core logic is sound, simpler than the old repeated unwraps, and covered by a regression test that would fail on the naive fresh-fetch-only version.
KEY INSIGHT:
Put shape normalization at the data boundary once; accessors should consume a stable internal representation, not compensate for transport quirks.
This PR review was generated by an AI agent (OpenHands) on behalf of the requester.
|
Done — I reviewed PR #248, verified the relevant Review link: #248 (review) |
|
🚀 Released in v1.32.0. |
Problem
RemoteStatehas 7 accessors that each unwrapped the API'sfull_statewrapper themselves (via a privateunwrapState()helper) on top of the result ofgetConversationInfo().The original issue framed the per-accessor unwrap as purely redundant. It isn't quite:
getConversationInfo()only normalized the fresh REST fetch, not the cache-hit return, and the cache can also be populated by WebSocket state-update events (createStateUpdateCallback→updateStateFromEvent) that still carry afull_statewrapper. So the per-accessor unwrap was load-bearing for the event-driven path.Closes OpenHands/software-agent-sdk#4733.
Change
getConversationInfo()the single unwrap point, normalizing on both the cache-hit and fresh-fetch returns (new privatenormalizeFullState()helper).The helper is
private, so this is internal-only — no public API or behavior change.Why the extra care (caught by CI)
My first cut normalized only the fresh fetch (matching the issue's literal proposal). Unit tests passed, but the integration test failed:
i.e. the cached state — populated from state-update events — still had the
full_statewrapper, and the cache-hit path returned it un-normalized. Normalizing on the cache-hit path fixes it.Tests
src/__tests__/remote-state.test.ts:full_state-wrapped REST payload;agent_statusfallback;full_statewrapper is normalized (this test fails on the naive fresh-fetch-only version and passes here — reproducing the integration failure locally).Full unit suite green (276 tests),
tscbuild clean, eslint (no new warnings) + prettier clean. Integration test expected to pass now that the event-populated-cache case is handled.