fix(conversational-skills): JS pro-code SDK fails to start on Node 22+ and leaks slot state across sessions - #333
Open
xentristech wants to merge 1 commit into
Conversation
…tate The pro-code mock server does not start on Node 22+, and once it does it leaks slot state between sessions and users. - LanguageManager used import assertions (`assert:`), removed in Node 22. Switched to `with:`, and the fallback error now keeps the original as `cause` -- it previously reported every failure as "bundle not found", which sends you looking for a file that is present. - app.js built the skill once at module load. Skill.orchestrate hands that same slotsInFlight reference to every response and the handler mutates it, so a validation error raised by one user was served to every later request, in every session. Build the skill per request instead. This also makes context.global.language reachable, so the non-English bundles are usable. - Per-request instances need slot visibility persisted, which the shared mutation had been providing by accident. Added rehydrateSlots/persistSlots using the same `visible_slots` and `<slot>_schema` keys the Java SDK already uses in SkillOrchestrator.initializeSlotHandlers. - An exception in orchestrate became an unhandled rejection and terminated the process, so one malformed request took the server down for everyone. Wrapped in try/catch returning 500. All 46 existing jest tests still pass. Verified on Node v24.14.1.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The JS pro-code skill mock server does not start on Node 22+, and once it does, it leaks slot state between sessions and users. This fixes both, plus a crash that takes down the whole process.
All 46 existing jest tests still pass.
Verified on Windows 11, Node v24.14.1, npm 11.11.0.
1. Server does not start on Node >= 22
LanguageManagerloads i18n bundles with import assertions:Node 22 removed
assert:in favour ofwith:. On Node 24 the import throws:The
catchthen replaces that with a misleading message, so the reported failure is:The bundle is there. I went looking for a missing file for a while before checking what the real exception was.
Fixed:
assert:->with:, and the fallback error now carries the original ascauseso the next person sees what actually happened.2. Slot state leaks across sessions and users
app.jsbuilt the skill once at module load:SkillholdsslotsInFlight,Skill.orchestratepasses that same reference into everySkillResponse, and the bluepoints handler mutates it:SlotsInFlight._slotsholdsSlotinstances, so that mutation is permanent and global.Reproduction against the unmodified server (mock balance is 1500):
validation_errorFrom step 2 onward every request to the server carries that error forever.
Fixed: build the skill per request, which is what the factory was already for.
Side benefit: it now honours
context.global.language, so thede/es/fr/ja/pt_brbundles are actually reachable. Previously everything was hardcoded to
en.3. Per-request instances need the visibility to be persisted
Fixing #2 alone breaks the multi-turn flow, because slot visibility only survived
because of the shared mutation.
Measured: user types "kim" (ambiguous), the selector slot appears. If on the next turn they
ignore the selector and answer the other prompt instead, the selector disappears — while
recipientLookupResultis still sitting in state. The ambiguity is never resolved,recipientstays "kim", and
onConfirmthen reads arecipientthat does not exist.The Java SDK already solves this:
SkillOrchestrator.initializeSlotHandlerspersistsvisible_slotsand<slot>_schemaintolocal_variablesand rehydrates them each request.watsonx round-trips
stateon every turn, so that is where the decision belongs.Fixed:
rehydrateSlots/persistSlotsinapp.js, using the same keys as the Java SDK.4. One exception kills the process
Skill.orchestratelogs and rethrows. The express handler wasasyncwith notry/catchand no
next(err), so the throw became an unhandled rejection and Node 24 terminated theprocess. One malformed request took the server down for every other user.
Fixed:
try/catchreturning 500.Verified end to end after the changes
recipientresolved intolocal_variablesskill_complete+ "Bluepoints transfer successful."skill_cancel+ "No rush, you can transfer bluepoints anytime you want."Cross-session check re-run after the fix: session B is no longer contaminated by session A.
Not addressed here
Skill.jsline 92 passesconversation_memorytoonLLMPassThru, but that identifier isnot a parameter of
orchestrateand is not in scope — that branch throwsReferenceErroras soon as it is used. I did not guess at whether it should come from
contextorstate.re-offered on later turns. That looked like demo business logic rather than a clear defect,
so I left it alone.
npm testinvokesnode_modules/.bin/jest, a bash script, which fails on Windows.Workaround:
node --experimental-vm-modules node_modules/jest/bin/jest.js.I authored all of the code in this PR and have the right to contribute it.
This is not part of an IBM offering or a contracted services engagement.
Investigation and implementation were done with AI assistance (Claude). Every claim above
was verified by running the code — the reproduction tables are measured output, not inferred.