LLM agent file_write calls are always blocked — safe_mode: false / context.global.unsafe: true never reach _unsafe_mode #115
Labels
No labels
auto/blocked-by-deps
auto/ci-timeout
auto/claimed-implementer
auto/claimed-merge
auto/claimed-reviewer
auto/driver-down
auto/invariant-violation
auto/last-attempt-tier-0
auto/last-attempt-tier-1
auto/last-attempt-tier-2
auto/last-attempt-tier-min
Automation Tracking
auto/needs-conflict-resolution
auto/needs-implementer
auto/postmortem
auto/ready-to-merge
auto/restart-throttled
auto/revert
auto/sentinel
auto/stale-inactivity
auto/unstable
Blocked
Bounty
$100
Bounty
$1000
Bounty
$10000
Bounty
$20
Bounty
$2000
Bounty
$250
Bounty
$50
Bounty
$500
Bounty
$5000
Bounty
$750
MoSCoW
Could have
MoSCoW
Must have
MoSCoW
Should have
Needs Feedback
Points
1
Points
13
Points
2
Points
21
Points
3
Points
34
Points
5
Points
55
Points
8
Points
88
Priority
Backlog
Priority
CI Blocker
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Signed-off: Owner
Signed-off: Scrum Master
Signed-off: Tech Lead
Spike
State
Completed
State
Duplicate
State
In Progress
State
In Review
State
Paused
State
Unverified
State
Verified
State
Wont Do
Type
Automation
Type
Bug
Type
Discussion
Type
Documentation
Type
Epic
Type
Feature
Type
Legendary
Type
Refactor
Type
Support
Type
Task
Type
Testing
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Depends on
#116 TDD: LLM agent file_write calls are always blocked — safe_mode: false / context.global.unsafe: true never reach _unsafe_mode
cleveragents/cleveractors-core
#129 fix(agents): honor safe_mode and context.global.unsafe for LLM-agent tool calls
cleveragents/cleveractors-core
Reference
cleveragents/cleveractors-core#115
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Metadata
Background and context
ADR-2030added tool-calling support totype: llmagents (docs/adr/ADR-2030-tool-calling-spec-extensions.md), but never addressed how the pre-existingsafe_mode/unsafe/_unsafe_modecontract (docs/index.md §4.5, §4.5.4, §9.4, §10.3) composes with LLM-agent-issued tool calls. Investigating whypackages/calculator-app-actor.yaml(an LLM agent withsafe_mode: falseandcontext.global.unsafe: true) can never write its generated files surfaced two compounding implementation gaps, both in the tool-call dispatch path — not a spec ambiguity.Current behavior
Every
file_writecall made by an LLM agent's tool-call loop fails, regardless of the actor's configuration:Reproduction:
packages/calculator-app-actor.yaml— atype: llmagent (calculator_builder) withtools: [file_read, file_write, shell],safe_mode: false, and an actor-levelcontext.global.unsafe: true.buildnode →calculator_builder) viacleveractors.runtime.create_executor.file_writetool call.ExecutionError("File writing requires unsafe mode").Root cause — two compounding bugs:
Dead config key in
cleveractors.agents.llm.LLMAgent. All three tool-call dispatch sites in the multi-turn tool loop (_execute_tool_loopand its two budget/stuck-model synthesis-retry mirrors) compute:before building the ephemeral per-call
ToolAgentand its context viaLLMAgent._build_tool_context(parent_unsafe). No code anywhere in the codebase (cleveractors.agents.factory.AgentFactory,cleveractors.core.application,cleveractors.runtime_dispatch) ever setsconfig["unsafe_mode"]— it is a key nobody writes. The actual field an actor author sets issafe_mode(docs/index.md §4.5) —packages/calculator-app-actor.yamlsetssafe_mode: falseoncalculator_builder, but the tool-call dispatch path never readsself.config.get("safe_mode", ...).parent_unsafeis therefore alwaysFalse,_build_tool_contextnever includes_unsafe_mode, andcleveractors.agents.tool.ToolAgent._file_write_tool(§4.5.4) always raises.Runtime-layer gap for the
unsafehost contract. Per §9.4/§10.3,context.global.unsafe: trueon an actor should be checked against whether the host was actually placed in unsafe mode — refusing execution withUnsafeConfigurationErrorif not, and propagating_unsafe_mode: trueinto every invocation context if so. This is correctly implemented incleveractors.core.application.Application._enforce_unsafe_flag(+ itsmetadata["_unsafe_mode"] = self.unsafecall sites). Butcleveractors.runtime_dispatch._execute_graph/_execute_graph_stream— the pathpackages/*.yamlactors run through viacleveractors.runtime.create_executor— has no host-unsafe-flag concept at all, andLLMAgent._build_tool_contextignores the graph'sglobal_contextentirely when building the per-tool-call context.Net effect: no LLM-agent-driven
file_writecan currently succeed through theExecutorruntime path, under any actor configuration.Expected behavior
safe_mode: falsecan successfully write files via model-issuedfile_writetool calls.context.global.unsafe: true, executed on a host actually placed in unsafe mode, propagates_unsafe_mode: trueinto every tool-call context reachable from the graph (§9.4/§10.3).UnsafeConfigurationError(§9.4) rather than surfacing a generic, always-onfile_writeExecutionError.safe_mode: true) sandboxing behavior forfile_read/shellis unaffected.Acceptance criteria
type: llm) withtools: [file_write]andsafe_mode: falsesuccessfully executes a model-issuedfile_writetool call.LLMAgent's tool-call dispatch derivesparent_unsafefrom the agent's ownsafe_modeconfig field, not the nonexistentunsafe_modekey.cleveractors.runtime.create_executorwithcontext.global.unsafe: truedeclared, on a host placed in unsafe mode, results in_unsafe_mode: truereaching every tool-call context in the graph.UnsafeConfigurationErrorinstead of afile_writeExecutionError.safe_mode: truerestrictions onfile_read/shell/directory traversal.Supporting information
docs/index.md§4.5 (Tool Agents,safe_modefield), §4.5.4 (Safe-Mode Restrictions), §9.4 (unsafe), §10.3 (_unsafe_modereserved context key).docs/adr/ADR-2030-tool-calling-spec-extensions.md— added LLM-agent tool calling but never addressedsafe_mode/unsafecomposition for it; a follow-up ADR revision may be warranted once the fix approach is settled.packages/calculator-app-actor.yaml.Subtasks
cleveractors.agents.llm.LLMAgent's three tool-call dispatch sites to deriveparent_unsafefromself.config.get("safe_mode", True) is False, not the deadunsafe_modekey.context.global.unsafedeclaration throughcleveractors.runtime_dispatch._execute_graph/_execute_graph_streaminto a host-unsafe-flag check (mirroringcleveractors.core.application.Application._enforce_unsafe_flag), raisingUnsafeConfigurationErrorwhen required but absent, and propagating_unsafe_mode: trueinto every tool-call context when present.safe_mode: falsepath and thecontext.global.unsafehost-mode path forfile_write, including theUnsafeConfigurationErrorrefusal case.file_writeend-to-end through theExecutorgraph path with an unsafe host.nox -s coverage_report.nox(all default sessions), fix any errors.Definition of Done
This issue is complete when:
tools_max_timeout/shell_max_timeoutfrom the parent LLM agent #140