feat: ambient per-call options (the caller-side half of IpcContext) - #156
Open
eduard-dumitru wants to merge 3 commits into
Open
eduard-dumitru wants to merge 3 commits into
eduard-dumitru wants to merge 3 commits into
Conversation
eduard-dumitru
requested review from
bogdanStan92,
danutboanta,
mihainradu and
mihaipetrisor82uip
September 14, 2026 23:08
IpcContext (#127) let a contract reach the peer without a Message parameter, so that a contracts assembly need not reference UiPath.Ipc. The caller side had no equivalent: a per-call deadline could only be set by passing a Message, which puts the transport type back in the signature and costs a wire slot. IpcCallOptions.With(timeout) in .NET and call_options(timeout) in Python apply a deadline to every call on the current async flow. Precedence is client default, then ambient, then an explicit Message argument, which still wins. The wire is unchanged: Request.TimeoutInSeconds already carries the value and peers already honour it, so this is a client API addition, not a protocol change. TypeScript is not included -- see the PR description for the open question.
Same shape as the .NET and Python halves: a scope that applies a deadline to every call made inside it, so a contract that takes no Message can still be bounded. Precedence is explicit Message, then ambient, then the client default. Node only, deliberately. Tracking "the current asynchronous flow" needs AsyncLocalStorage and browsers have no equivalent, so `callOptions` is exported from the node entry point alone -- importing it from the web build is a compile error rather than a silent no-op. The read side sits in std because RpcRequestFactory is shared code and cannot import from a platform entry point; with no provider installed it returns undefined and the web build behaves exactly as before.
The two new tests read the written frame after one `sleep(0)`, which is enough on a fast machine and not on a CI agent -- both failed on Linux py310/py311 with an IndexError off `frames[0]`. Poll until the frame lands, as the sibling test in this file already does, and fail with a message that says what happened.
eduard-dumitru
force-pushed
the
feat/ambient-call-options
branch
from
September 15, 2026 22:49
fa658dd to
9c9161a
Compare
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.
Follow-up to #127. That PR gave the receiver a way to reach the peer without a
Messageparameter (IpcContext.Current), so a contracts assembly need not referenceUiPath.Ipc. The caller side had no equivalent: the only way to set a per-call deadline is to pass aMessageargument — which puts the transport type back into the signature and costs a wire slot.This adds the missing half.
Precedence
client default → ambient → explicit
Messageargument (still wins).The wire is unchanged
Request.TimeoutInSecondsalready carries the value and peers already honour it (Request.GetTimeout). This is purely a client-API addition — no protocol change, no version boundary between peers, only a package bump.Why now
uipath-pythonstreams job logs to the handler over a shared contract. Bounding those sends currently requires a trailingMessage, which forcedUiPath.IpcintoGenericExecutors.Contracts— exactly the dependency #127 existed to avoid.Tests
TimeoutInSecondscarries the ambient, and that an explicitMessageoverrides it). Full client suite: 107 passed.TypeScript — now included, Node only
Added in a second commit. Same shape as the other two halves:
Deliberately Node-only. Tracking "the current asynchronous flow" needs
AsyncLocalStorage, and browsers have no equivalent. So:callOptions/withRequestTimeoutare exported from the node entry point only — importing them from the web build is a compile error, not a silent no-op.AmbientCallOptions) has to live instd, becauseRpcRequestFactoryis shared code and cannot import from a platform entry point. It is a one-function seam with a comment saying so; with no provider installed it returnsundefinedand the web build behaves exactly as before. Bothtsconfig.node.jsonandtsconfig.web.jsontypecheck clean.We have no web end-users today, so the asymmetry costs nothing; if that ever changes, the seam is the place to add a browser implementation (TC39
AsyncContext, when it lands).Verification: the new spec (
test/node/core/CallOptions.test.ts) passes — 5 of 5 — and is mutation-checked: makingAmbientCallOptions.current()always returnundefinedfails 3 of them.It has to be run with the spec list narrowed, though, because the jasmine suite does not compile on
master, independent of this PR:Reproduced on a pristine
origin/masterworktree after a cleannpm ci— same two errors, same lines.tsconfig.jasmine.jsonsets noinclude, so it compilestest/as well, whichtsconfig.node.jsonnever does; that is why the build is green while the test run is not. Not fixed here — flagging it as a separate problem worth its own ticket.