Repository navigation
fix(code-execution): codeExecution.enabled takes only true or false - #485
Merged
nissa-seru merged 2 commits intoOct 18, 2026
Merged
Conversation
…nima-research#473) create() read config.codeExecution?.enabled by truthiness twice: to decide whether validateCodeExecutionConfig runs, and to decide whether the tool is synthesized. So "enabled": "false", as a JSON config or a ${VAR} substitution can write it, gave agents the code_execution tool and a python interpreter on the host, and a falsy non-boolean such as 0 was read as off without a word. validateCodeExecutionConfig now refuses an enabled that isn't true, false or null, naming the value's kind rather than the value, worded as the MCPL server boolean refusals are (kindOf, now exported from transport.ts, which the package entry doesn't re-export). null counts as not set. create() runs the validator whenever codeExecution is given, and the validator checks the rest only once the switch is true, so a configuration that isn't on still has its other settings left unchecked, as before. The tool is synthesized only for enabled === true. Tests: create() refuses a quoted "false", a token-like string, 0 and an object, each by kind and without the value; a null enabled, and an enabled false with contradictory limits, both create a framework without the tool. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hten the tests and the text Answers the internal review of the previous commit. A codeExecution that isn't an object, such as a bare `true`, read its switch as undefined and so was off, without a word: the same misread one level up. validateCodeExecutionConfig now refuses it, naming its kind, as the switch's refusal does. Text: the switch's doc and the validator's doc say null or leaving it out counts as off; scriptTimeLimits' comment says a ceiling below the default is refused when a framework with code execution on is created; the changelog fragment names the parent's refusal and, as anima-research#451's does, what a host that passes the setting on unchecked now gets. Tests: each refusal's whole message is asserted, so none can show the value, and nothing may exist at storePath after it. A new test refuses a codeExecution of true, "true" and an array. The off test creates a framework with codeExecution null, and with contradictory limits beside an enabled that is null, false or left out, inside its try. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Required label not found on this PR. |
1 task done
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
This was referenced Oct 18, 2026
nissa-seru
pushed a commit
to nissa-seru/agent-framework
that referenced
this pull request
Oct 18, 2026
main moved 71 merges since e670363. Beyond the conflicts, this is where they meet this branch. One rule for server ids, this branch's, across both engines: - A server id is held while a connect or a disconnect of it is in flight, or while a restart moves its server to it (mcpServerIdTaken, holdMcpServerId). anima-research#469's refuseMcplConnect and anima-research#506's mcplConnecting set went, so two rules don't guard the same ids. - A connect or a restart into an id whose connect is in flight, and whose server isn't registered yet, is refused in anima-research#506's words: 'MCPL server "<id>" has a connect in flight; try again once it settles'. - A disconnect made while a connect is in flight ends that connect and removes what it registered, where anima-research#506 refused the disconnect. - anima-research#469's prefixesMeet joins mcplPrefixCollision. It is checked at runtime connects and restarts in both engines, and a prefix that meets one a restart holds is refused too. create() already checks every configured server, a modern one included. - A tool call while its server connects reads anima-research#506's "connecting" text when a connect holds the id or a restart claims it (mcplServerAbsentText). Request errors (anima-research#247, in 0.22.3, and anima-research#506): - McplRequestError has no code, as 0.22.3 has it. The modern engine's two error-response failures pass their data alone, and the code stays in the message. The class this branch's first commit added is gone, so there's one. - The legacy engine's request errors take anima-research#506's words. The not-sent refusal after a stdio pipe failure says this host can no longer write to the server, and the method and the pipe's error go to the log. - protocolVersionRejection reads the server's message with describeCause, bounded, as the initialize error does. A message with no primitive form threw out of the handshake's line listener. Tool results: - An MCP tool's error result with no words, or only whitespace, reads as anima-research#506's text in both readings and on the direct path (mcplToolNoWordsText; normalizeStandardToolResult takes it as noWords). - The direct path's failure is mcplToolFailure's text without the server's other blocks, as 0.22.3 gives it, with structured kept by presence. A script's structured failure keeps its content. - anima-research#510's in-process registration: autoFetchReferences takes a mode, 'register', 'model' or 'script', in place of a positional boolean whose meaning differed on each side. executeMcplToolCall registers an MCPL peer's references only. The model's dispatch fetches eagerly, and a script's reads fetch everything. - A script's call of a tool that isn't an MCPL server's (a module's) materializes the references registered against their servers again, as main does. An MCPL server's were fetched at dispatch. - referenceBlockForScript gives a block the classifier rejects anima-research#506's notice (classifyBlock's shown), as the model's lanes do. Inbound handlers: - Main's containedInboundHandler replaces this branch's answerOnce and handlerFailureText. What a failure records, in its trace and its log line, goes through the server's redactor, which wireMcplEvents and a queued manifest run (rerunManifestRefresh) pass. It defaults to the text as it is. Transport (anima-research#494, anima-research#499, anima-research#485): - StdioTransport takes main's constructor, (child, rl, serverId), with both pipeFailure and stderrEnded. The handshake's wait for stderr's end comes through unchanged. - The boolean settings check is main's assertBooleanSetting, keeping this branch's dialsNetwork skip for inheritEnv. Elsewhere: - stop() is main's step()-by-step teardown, with endConnectsInFlight as its first step. - addModule checks the tool prefix, then resetCompletedStop('addModule()') (anima-research#532). - thrownText is main's boundedErrorMessage, with this branch's redactor applied before the cut. - Changelog: this branch's fragments say what changes from 0.22.3. The folded mcpl-request-outcome fragment is main's, and anima-research#506's connect-in-flight fragment describes the merged rule. - Tests: main's new stub servers answer protocolVersion. Words and log lines follow the merged code. The test stand-ins use the merged constructor, and wireMcplEvents gets its config. A close that now waits for the child is awaited after its request's rejection is observed. Two manifest re-run tests count the configuration read at each run's start. anima-research#506's test of refuseMcplConnect now tests the merged rule. Receipts, on Linux at this commit's tree: the typecheck and the build are clean, and every test file, each in a process of its own, passes: 232 files and 4,339 tests (4,322 passing, 17 skipped, none failing) on Node 24.22.0, and the same on Node 20.20.2, with CI's stop pair passing on both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
codeExecution.enabledtakes onlytrueorfalse, or can be left out, withnullcounting as left out.create()refuses any other value, and acodeExecutionthat isn't an object, so a quoted"false"no longer turns code execution on.!Fixes #473
Problem
On
main(e6703634),create()readsconfig.codeExecution?.enabledby truthiness twice:validateCodeExecutionConfigruns (src/framework.ts:2303);codeExecutionConfigis the config when the switch is truthy andnullotherwise (:2639), and thecode_executiontool and its interpreter are gated on that field (:4291,:14991).So
"enabled": "false", as a JSON config or a${VAR}substitution can write it, gives agents thecode_executiontool and a python interpreter on the host. A falsy non-boolean, such as0, is read as off, and nothing says so. So is a barecodeExecution: true, whose switch reads as undefined. The type and its doc say the switch is a boolean and the tool is "only synthesized when true" (src/types/framework.ts:44-45).Changes
validateCodeExecutionConfigrefuses anenabledthat isn'ttrue,falseornull, naming the value's kind rather than the value, as the MCPL server boolean refusals do. A string gets "true or false, unquoted".nullcounts as not set.codeExecutionthat isn't an object, such as a baretrue, is refused too, naming its kind.create()runs the validator whenevercodeExecutionis given, and the validator checks the other settings only onceenabledistrue. So a configuration that isn't on still has its other settings left unchecked, as before.enabled === true.kindOfis exported fromsrc/mcpl/transport.tsfor the validator's message. The package entry doesn't re-export it.enableddoc says what's refused, and a changelog fragment under Fixed.What callers will notice
create()now refuses acodeExecution.enabledsuch as"false","true",0,1or an object, with "codeExecution.enabled must be true or false, … not a string" (or the value's kind). Such a config used to start, with code execution on for a truthy value and off for a falsy one.codeExecutionthat isn't an object, such astrue, with "codeExecution must be an object, such as { enabled: true }, not a boolean". It used to start with code execution off.enabled: true,false,nullor left out behave as before.validateCodeExecutionConfigandkindOfaren't exported from the package entry.What each part is for
=== truealone. Reading onlytrueas on would make"false"off, but"true"off too, without a word. The refusal says atcreate()which value was misread. It's the rule fix(mcpl): an MCPL server's boolean settings are true or false, and anything else is refused (#379) #451 set for an MCPL server's booleans.nullas not set. A generated config can write a setting it leaves unset asnull, and this keeps such a config working, as fix(mcpl): an MCPL server's boolean settings are true or false, and anything else is refused (#379) #451 does.codeExecutionthat isn't an object. Its switch would read as undefined, so it would be off without a word, the same misread one level up.codeExecutionis given. Called only when the switch is truthy, as onmain, it would never see a falsy non-boolean such as0.=== trueat the gate, beside the refusal. With the refusal in front of it, a non-boolean reaches the gate only if the caller changes the config whilecreate()runs, so taking it out alone fails no test. It keeps the gate meaning what the type says.kindOfrather than copying it, so both refusals word a kind the same way.create()with a quoted"false", a token-like string,0and an object as the switch. Another runs it withtrue,"true"and an array ascodeExecution. Each checks the refusal's whole message, so no value can show, and that nothing was left at the store's path.nullcodeExecution, and contradictory limits beside anenabledthat isnull,falseor left out, each create a framework that doesn't offercode_execution.Tests
At
2e117acf, on Linux, Node 24.22.0 and Python 3.13.5. The pull request's CI runs the whole suite on Node 20 and 24.test/code-execution.test.tsandtest/mcpl-config-booleans.test.tspass 64 and 8 tests.main's four changed source files, 2 of those fail: the two refusal tests, which get no rejection. In that state, a probe that creates a framework withenabled: "false"findscode_executionoffered, and a call to it runs python and prints its version.nullas not set, or leaving the switch out as not set: the off test;enabled === false: the off test, whose off configurations are then refused for their limits;main's truthy one: both refusal tests;nullcodeExecutionthrough to the validator: the off test;=== true, taken out alone, fails no test, as "What each part is for" says (at8b3acaf3, whose gate is this head's).create()rejects with "codeExecution.enabled must be true or false, unquoted, not a string: true gives agents the code_execution tool, …".Not verified
Related
validateCodeExecutionConfig, to refuse aninheritEnvthat isn't true, false or null, and anenvthat isn't an object of string values, and exportskindOf, with its own doc and its own answers fornullandundefined. So the two conflict in both files. The two validators' refusals are separate checks, and fix(code-execution): the python runner gets the child allowlist and its own working directory (follow-up to #175) #193'skindOfcovers every case this change's needs.Written by Effie-2756 (Claude Opus 5.5) using the Rookery's Pi tooling, on Ada-1017's calls. Naomi-2422 (Claude Opus 5.5) reviewed the change.