Repository navigation
feat(uipath-agents): coded-agent guardrails inspect files [AL-600] - #4453
apetraru-uipath wants to merge 5 commits into
Conversation
Built-in guardrails in coded agents can now read the files a run carries: `detection_scope` on @guardrail(...) and the middleware (uipath-platform 0.2.43, uipath-langchain 0.18.53). The coded guardrail references never mentioned files. The SDK docs the skill fetches in Step 0 describe both, but the fetched summaries often drop detection_scope and rarely carry how a typed Attachment is made visible to the guardrails. - new coded/capabilities/guardrails/files-guide.md: where detection_scope goes and where it never goes, the default (text only), the list-driven availability rule, making a typed Attachment visible with render_attachments_block, what each check reads, when to pick Text / Files / Both (per guardrail target, so not on a Tool-scope guardrail whose tools carry no file), three recipes (uploaded files, a tool that takes a file, a produced file), behaviour, testing with a real upload, What NOT to Do; BYO guardrails are called out as having no file support - guardrails.md: Step 0 makes files-guide.md and the tenant entry the authority for detection_scope, the tenant check filters to the one validator's Status, scopes, stages and parameter ids, BYO notice, Step 2 file surfaces, a short "Files and attachments" hook, Critical Rule 20 - guardrails-recommend.md: file surfaces, File protection row, the detection_scope decision, correctness row, coverage-gap and misapplied cases, Critical Rule 17 - file-attachments.md, SKILL.md routing row, the low-code files guide's availability row point to the new guide - uipath-review: file-coverage and misapplied-file-scope cases under the existing coded rule ids Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eferences Found while adding file support; unrelated to it. - sdk-services.md showed sdk.guardrails.evaluate(guardrail_name=..., data=..., folder_path=...), which does not exist; the method is evaluate_guardrail(input_data, guardrail) - guardrails-recommend.md Validate Mode said middleware takes no stage=; only the fixed-stage middleware (user prompt attacks, IP) don't, PII/harmful/judge/BYO accept it and deterministic requires it - guardrails.md said deterministic middleware defaults stage= to PRE_AND_POST; its stage is a required argument - guardrails.md's escalation example labelled PII middleware as taking no stage= - the SimpleCodedAgent and MisplacedPromptAttacksAgent test fixtures pinned uipath-langchain>=0.9.26; raised to 0.18.53 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- pii_middleware and user_prompt_attacks_decorator now ask for the typed question and an uploaded file; their checkers also require an Attachment field on Input, imported from uipath.platform.attachments, rendered into the user message, and detection_scope == Both on the Agent-scope PII middleware / the @guardrail carrying UserPromptAttacksValidator - new file_tool_judge: an LLM-as-judge on the review_contract tool of a new ContractIntakeCodedAgent fixture (Attachment input, tool that takes the file). Accepts @guardrail on the tool or UiPathLLMAsJudgeMiddleware at Tool scope with tools=[review_contract] wired into create_agent; requires the uipath_langchain.guardrails import, detection_scope == Files, BlockAction(detail=<the requested message>), a stage that includes PRE, the 12-month non-compete rule, a real model id, and the llm-as-judge-models call - new _shared/coded_file_guardrail_assertions.py (AST): resolves DetectionScope members, exact strings, DetectionScope("...") and module constants (rejects lowercase and other enums); the render check requires <state>.<field>.model_dump(mode="json") or an {ID, FullName, MimeType} dict built from the input field, flowing into the HumanMessage content; CODEOWNERS line added - the PII and user prompt attacks checkers read scopes, tools, action, stage and entities off the guardrail call itself instead of searching the file text, and only count middleware wired into create_agent; the decorator must sit on the LLM factory at GuardrailExecutionStage.PRE 58 crafted pass/fail graph.py variants grade as expected. Every passing variant imports against uipath-langchain 0.18.55 with its target wrapped, and the accepted reference shapes reach the guardrail at runtime (build_registry finds the file); {id, fileName, mimeType} and model_dump() without mode="json" do not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Claude finished @apetraru-uipath's task in 1m 47s —— View job SummaryAdds coded-agent file inspection for guardrails ( Change-by-Change Review1.
|
| Area | Status | Notes |
|---|---|---|
| Frontmatter | OK | Unchanged |
| E2E Tests | Issue | file_tool_judge passes an unwired call-form decorator (#2) |
| Skill Body | OK | One routing row added |
| References & Assets | Issue | The detection_scope contract is restated across 5+ places (#1) |
| Repo Hygiene | OK | CODEOWNERS updated, no secrets or personal paths; the uipath-review edits are scoped to the same feature |
Issues for Manual Review
- SDK accuracy I could not check here:
render_attachments_blockandDetectionScopeare exported fromuipath_langchain.guardrails.UiPathDeterministicGuardrailMiddleware(detection_scope=...)raisesTypeError.- Rule 4: an invalid string raises
ValueErrorat import. sdk.guardrails.evaluate_guardrail(input_data=..., guardrail=BuiltInValidatorGuardrail(...))uses those constructor kwargs.upload_async(name=, source_path=)is the signature.
- Whether
UiPathPromptInjectionMiddlewarealso acceptsdetection_scope. Rule 1 offiles-guide.mdleaves it out of the middleware list. - The "What each check reads" table's claims about runtime semantics (Tool · PRE reads only the files the arguments name; a decorator on a graph without a
messageskey is not checked).
Conclusion
The content is solid and the RED → GREEN evidence fits the change. Two Medium items should be fixed before merge: trim the duplicated detection_scope contract in guardrails.md (#1), and close the unwired call-form decorator gap in check_file_tool_judge.py (#2). Everything else is Low polish.
|
Claude finished @apetraru-uipath's task in 59s —— View job Coder-eval task lint (advisory)3 task YAMLs changed. Verdicts: 0 Critical, 0 High, 0 Medium, 2 Low, 1 OK. Rubric: .claude/commands/lint-task.md. This check is advisory and never blocks merge. Evidence of passing run
Per-task lint
|
…niel-joica ctiliescuuipath is replaced on every guardrail entry: the low-code and coded guardrail references, the uipath-review guardrail references, the guardrail test tasks and the shared guardrail checkers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guardrail checkers, and the helpers only they import (guardrail_middleware.py, file_guardrail_assertions.py, coded_file_guardrail_assertions.py), move from tests/tasks/uipath-agents/_shared/ to _shared/guardrails/. The task commands and imports follow. One folder rule in CODEOWNERS replaces the nine per-file rules and also covers the two helpers that fell to the uipath-agents default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Why
Built-in guardrails in coded agents can read the files a run carries since uipath-platform 0.2.43 / uipath-langchain 0.18.53:
detection_scope(Text/Files/Both) on@guardrail(...)and on the middleware. The coded guardrail references never mentioned files. The SDK docs the skill fetches in Step 0 now describe the argument (published 2026-11-05 by UiPath/uipath-python#1975 and UiPath/uipath-langchain-python#1195). An agent working from those docs alone still often misses how a typedAttachmentreaches the guardrails, and the fetched summaries regularly dropdetection_scopefor user prompt attacks.Low-code counterpart: #3857. Ticket: AL-674, the coded half of AL-600.
What changed
uipath-agents
New
references/coded/capabilities/guardrails/files-guide.md. It covers:detection_scopegoes: never on a validator, BYO, deterministic orCustomValidatorguardrail;Attachmentwithrender_attachments_block;"BYO guardrails have no file support" is a notice at the top of the file.
guardrails.md:files-guide.mdand the tenant entry the authority fordetection_scope. A fetched summary that omits it is not a reason to drop it.--output-filter, so itsParametersis never cut off.guardrails-recommend.md: file surfaces, a File protection row, thedetection_scopedecision, a correctness row, coverage-gap and misapplied-file-scope cases, Critical Rule 17.One-line pointers in
file-attachments.md, in theSKILL.mdrouting table (body only), and in the low-code files guide's availability row.uipath-review gets the file-coverage and misapplied-file-scope cases under the existing
CODED_GUARDRAIL_ACTION_INEFFECTIVE/CODED_GUARDRAIL_MISAPPLIEDids, plus a GuardrailsChecker note. There are no new rule ids.Separate commit, unrelated to files: stale statements.
sdk-services.mdshowed asdk.guardrails.evaluate(...)that does not exist; the method isevaluate_guardrail(input_data, guardrail).stage=.guardrails.mdsaid deterministic middleware'sstagedefaults toPRE_AND_POST; it is required. It also labelled PII middleware as taking nostage=.uipath-langchain>=0.9.26to>=0.18.53. uv installs the latest release either way, so no other task changes what it installs.Separate commit: code owners. Every guardrail entry in
CODEOWNERSlistsandreizdrali-uipathanddaniel-joicain place ofctiliescuuipath.Separate commit: guardrail checkers move to
tests/tasks/uipath-agents/_shared/guardrails/.guardrail_middleware.py,file_guardrail_assertions.pyandcoded_file_guardrail_assertions.py.run_commandpaths and the imports.CODEOWNERSreplaces the nine per-file rules.guardrail_middleware.pyandfile_guardrail_assertions.pywere owned by the/tests/tasks/uipath-agents/default (coded-agents team) and now belong to the guardrails owners. Only guardrail checkers import them.Tests
pii_middlewareanduser_prompt_attacks_decorator(e2e) now ask about both the typed text and an uploaded file. Their checkers also require anAttachmentinput rendered into the user message, anddetection_scope == Both.file_tool_judge(e2e) runs on a newContractIntakeCodedAgentfixture. It requires an LLM-as-judge on thereview_contracttool withFiles, PRE, a block action and a real judge model._shared/guardrails/coded_file_guardrail_assertions.py. Its render check requires<state>.<field>.model_dump(mode="json"), or an{ID, FullName, MimeType}dict built from the field, flowing into theHumanMessage. At runtime those are exactly the shapes that reach the guardrail (build_registryfinds the file).{id, fileName, mimeType}doesn't reach it, andmodel_dump()withoutmode="json"raises.create_agent. They no longer search the file text. Several wrong variants used to pass:PRE_AND_POST,BlockActiononly in a comment, the decorator on a tool, and an unwired middleware.graph.pyvariants grade as expected. Every passing variant imports against uipath-langchain 0.18.55 with its target wrapped.Runs
Setup: claude-code +
claude-sonnet-5,experiments/default.yaml, tempdir driver,-D 'agent.setting_sources=[]', 3 repeats per task. The alpha org has file scope on every built-in validator. RED uses the skills frommain(4b7f96d); GREEN uses this branch. Both are graded with this branch's final checkers: the RED runs were re-graded, and the GREEN runs ran on the final branch.How to read this:
detection_scopeitself is not what differs. Every RED run set the expected value on the right call, using the SDK docs plus the tenant catalog.Attachment, never rendered it, or rendered a hand-built reference the guardrail cannot read. That last run passed the old checker. All 9 GREEN runs readfiles-guide.mdand passed the render check, and all 9 ran the filtered tenant check.file_tool_judgeis a regression guard, not a discriminating test. Its fixture already renders the upload, and RED reached the same code from the SDK docs.pii_middlewareruns the Tool-scope middleware also gotBoth, which the new per-target row says to omit. The prompt asks for both scopes literally, andBothdoes nothing extra on a tool that takes no file.Ran
skill-agent-guardrail-coded-pii-middlewarelocally and it passed.Ran
skill-agent-guardrail-coded-user-prompt-attacks-decoratorlocally and it passed.Ran
skill-agent-guardrail-coded-file-tool-judgelocally and it passed.Only these three tasks were run; the other coded guardrail tasks were not rerun.
How the checker move was verified:
coder-eval evaluate, once against the tree before the move and once after it. The inputs were each task's fixture plus up to 9 distinct saved workspaces. All 116 pairs grade identically (63 pass).detectionScoperename. With that rename applied, all 9 grade the same, and pass, before and after the move.pytest tests/tasks/uipath-agents/passes (29), and the 58 crafted checker cases still grade as expected.Repo gates:
skills:validate,skills:build,skills:check-links, the description hook andscripts/check-cli-verbs.pypass.npm run skills:testhas 3 failures, and they fail identically onmain:classifies UIA, productivity, step and unknown results…,pack builds complete…, andthe built studioweb flavor mentions no new command….Follow-ups (not this PR)
UiPathUserPromptAttacksMiddleware, becausedetection_scopeappears only in the common-parameter list. In 5 of 6 runs, fetched summaries read that as "no file support".file_tool_judge: a fixture that does not pre-render the file.🤖 Generated with Claude Code