Skip to content

feat(uipath-agents): coded-agent guardrails inspect files [AL-600] - #4453

Open
apetraru-uipath wants to merge 5 commits into
mainfrom
feat/uipath-agents-coded-guardrail-files-al600
Open

apetraru-uipath wants to merge 5 commits into
mainfrom
feat/uipath-agents-coded-guardrail-files-al600

Conversation

@apetraru-uipath

@apetraru-uipath apetraru-uipath commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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 typed Attachment reaches the guardrails, and the fetched summaries regularly drop detection_scope for 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:

    • where detection_scope goes: never on a validator, BYO, deterministic or CustomValidator guardrail;
    • the text-only default and the list-driven availability rule;
    • rendering a typed Attachment with render_attachments_block;
    • what each check reads (middleware vs decorator);
    • choosing Text / Files / Both per guardrail target;
    • three recipes: uploaded files, a tool that takes a file, a produced file;
    • behaviour, testing with a real upload, and What NOT to Do.

    "BYO guardrails have no file support" is a notice at the top of the file.

  • guardrails.md:

    • Step 0 makes files-guide.md and the tenant entry the authority for detection_scope. A fetched summary that omits it is not a reason to drop it.
    • The tenant check filters to the one validator with --output-filter, so its Parameters is never cut off.
    • BYO notice, file surfaces in Step 2, § Files and attachments, Critical Rule 20.
  • guardrails-recommend.md: file surfaces, a File protection row, the detection_scope decision, a correctness row, coverage-gap and misapplied-file-scope cases, Critical Rule 17.

  • One-line pointers in file-attachments.md, in the SKILL.md routing 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_MISAPPLIED ids, plus a GuardrailsChecker note. There are no new rule ids.

Separate commit, unrelated to files: stale statements.

  • sdk-services.md showed a sdk.guardrails.evaluate(...) that does not exist; the method is evaluate_guardrail(input_data, guardrail).
  • Validate Mode said middleware takes no stage=.
  • guardrails.md said deterministic middleware's stage defaults to PRE_AND_POST; it is required. It also labelled PII middleware as taking no stage=.
  • The fixture floors move from uipath-langchain>=0.9.26 to >=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 CODEOWNERS lists andreizdrali-uipath and daniel-joica in place of ctiliescuuipath.

Separate commit: guardrail checkers move to tests/tasks/uipath-agents/_shared/guardrails/.

  • What moved: the 8 shared guardrail checkers, plus guardrail_middleware.py, file_guardrail_assertions.py and coded_file_guardrail_assertions.py.
  • What follows them: the task run_command paths and the imports.
  • Ownership: one folder rule in CODEOWNERS replaces the nine per-file rules. guardrail_middleware.py and file_guardrail_assertions.py were 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_middleware and user_prompt_attacks_decorator (e2e) now ask about both the typed text and an uploaded file. Their checkers also require an Attachment input rendered into the user message, and detection_scope == Both.
  • New file_tool_judge (e2e) runs on a new ContractIntakeCodedAgent fixture. It requires an LLM-as-judge on the review_contract tool with Files, PRE, a block action and a real judge model.
  • New _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 the HumanMessage. At runtime those are exactly the shapes that reach the guardrail (build_registry finds the file). {id, fileName, mimeType} doesn't reach it, and model_dump() without mode="json" raises.
  • The two existing checkers now read scopes, tools, action, stage and entities off the guardrail call itself, and they count only middleware wired into create_agent. They no longer search the file text. Several wrong variants used to pass: PRE_AND_POST, BlockAction only in a comment, the decorator on a tool, and an unwired middleware.
  • 58 crafted pass/fail graph.py variants 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 from main (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.

Task RED GREEN Median tool calls RED → GREEN Median duration Median input tokens
pii_middleware 2/3 3/3 24 → 17 236 → 195 s 8.9 → 5.4 M
user_prompt_attacks_decorator 1/3 3/3 30 → 17 256 → 109 s 11.5 → 5.6 M
file_tool_judge 3/3 3/3 20 → 21 143 → 127 s 6.1 → 7.4 M

How to read this:

  • detection_scope itself is not what differs. Every RED run set the expected value on the right call, using the SDK docs plus the tenant catalog.
  • RED loses on getting the uploaded file to the guardrails. 3 of 9 RED runs never declared the 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 read files-guide.md and passed the render check, and all 9 ran the filtered tenant check.
  • The gap is directional, not significant. With n = 3 per arm, the 9/9 vs 6/9 difference sits entirely on two tasks.
  • file_tool_judge is a regression guard, not a discriminating test. Its fixture already renders the upload, and RED reached the same code from the SDK docs.
  • These runs prove structure, not live blocking. The prompts forbid uploads, so the evidence is that the files import with their targets wrapped. One GREEN run uploaded a synthetic statement anyway, and the PII guardrail flagged the email and phone "in statement.txt". After the runs I added a line to the testing section: run a live test only when the user wants one.
  • One GREEN deviation. In all 3 GREEN pii_middleware runs the Tool-scope middleware also got Both, which the new per-target row says to omit. The prompt asks for both scopes literally, and Both does nothing extra on a tool that takes no file.

Ran skill-agent-guardrail-coded-pii-middleware locally and it passed.
Ran skill-agent-guardrail-coded-user-prompt-attacks-decorator locally and it passed.
Ran skill-agent-guardrail-coded-file-tool-judge locally and it passed.
Only these three tasks were run; the other coded guardrail tasks were not rerun.

How the checker move was verified:

  • Same grades on saved workspaces. I graded saved workspaces of all 13 guardrail tasks touched by the move with 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).
  • Low-code pass path. The saved low-code workspaces predate the detectionScope rename. With that rename applied, all 9 grade the same, and pass, before and after the move.
  • Tests. 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 and scripts/check-cli-verbs.py pass. npm run skills:test has 3 failures, and they fail identically on main: classifies UIA, productivity, step and unknown results…, pack builds complete…, and the built studioweb flavor mentions no new command….

Follow-ups (not this PR)

  • uipath-langchain docs: the middleware table lists "no extra parameters" for UiPathUserPromptAttacksMiddleware, because detection_scope appears only in the common-parameter list. In 5 of 6 runs, fetched summaries read that as "no file support".
  • A discriminating file_tool_judge: a fixture that does not pre-render the file.
  • The skills-internal coded scope still open on AL-674.

🤖 Generated with Claude Code

apetraru-uipath and others added 3 commits November 5, 2026 18:00
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>
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @apetraru-uipath's task in 1m 47s —— View job


Summary

Adds coded-agent file inspection for guardrails (detection_scope: Text / Files / Both) to uipath-agents. It adds a new files-guide.md, pointers from guardrails.md, guardrails-recommend.md, file-attachments.md and SKILL.md, and the matching audit cases in uipath-review. A separate commit fixes stale SDK statements. Tests: one new e2e task, two e2e tasks extended to cover uploaded files, and a shared AST assertion module.

Change-by-Change Review

1. skills/uipath-agents/references/coded/capabilities/guardrails/guardrails.md § Files and attachments (L391-399) plus Critical Rule 20 (L536)

Medium: duplication / reference pointer discipline
The new section links to files-guide.md, but it also restates that guide's numbered rules 1, 2, 5, 6 and What NOT to Do 1, 2 and 4: where the argument goes, list-driven availability, the text-only default, the render requirement, and no BYO / deterministic / CustomValidator. Critical Rule 20 says it again. The same rule set also appears in:

  • the new BYO callout (L103);
  • guardrails-recommend.md Step 7 "detection_scope decision", its correctness row, and Critical Rule 17.

That is 5 or more copies of one contract. Keep Critical Rule 20, since Critical Rules are meant to be terse. Reduce § Files and attachments to one bridging sentence plus the link. The Step 2 bullet (L189) and the Step 0 callout (L43) are fine as routing.
Fix this →

2. tests/.../file_tool_judge/check_file_tool_judge.py L59-71 (decorator_guardrails)

Medium: checker false positive
The call-form branch accepts guardrail(...)(review_contract) anywhere in the file. It never checks that the wrapped result replaces the tool passed to create_agent(tools=[...]). So guarded = guardrail(...)(review_contract) with tools=[review_contract] left unchanged passes, even though nothing is wrapped. That is the same kind of unwired variant the PR fixed for middleware. Require one of two things: the call result is assigned back to review_contract, or it (or the name it is assigned to) appears in a create_agent(tools=[...]) list.

On the @-decorator branch, decorator order is also unchecked: @tool above @guardrail(...) passes. guardrails.md requires @guardrail above @tool. This one is Low.
Fix this →

3. guardrails-recommend.md L293 (Actionability step 3)

Low
The fixed-stage list names UiPathUserPromptAttacksMiddleware and UiPathIntellectualPropertyMiddleware but leaves out UiPathPromptInjectionMiddleware, which guardrails.md L295 lists as fixed-stage PRE. Add it so Validate Mode does not flag a missing stage= on it.

4. files-guide.md

OK, with Low notes:

  • The structure is good: a top notice, numbered rules, a target table, recipes, testing, What NOT to Do, and References.
  • Every relative link and anchor resolves: #supported-types-and-caps, #check-tenant-availability-mandatory-for-built-in-ai-validators, and file-attachments.md § Local Testing.
  • The Low-code ↔ coded links stay inside the uipath-agents skill, so they are fine.
  • Low: Recipe 3 is a bare *UiPath…Middleware(...), fragment. One line such as # inside create_agent(middleware=[...]) would stop agents from pasting it at module level.
  • Low: the upload snippet in Testing step 1 uses top-level await. Wrap it in asyncio.run(...) or note that it must run inside an async function.

5. guardrails.md tenant check (L69)

OK. --output-filter is already used elsewhere in the skill. Filtering on !IsByo and projecting Parameters[].Id is what the detectionScope check needs.

6. sdk-services.md, stage corrections, fixture floor bumps

OK. These correct stale statements. The evaluate_guardrail(input_data, guardrail) signature needs manual confirmation (see below).

7. uipath-review changes

OK. No new rule ids. /uipath:uipath-agents follows the existing cross-skill mention style in that skill: a name, not a file read.

8. Tests (pii_middleware, user_prompt_attacks_decorator, file_tool_judge, _shared/coded_file_guardrail_assertions.py)

OK apart from #2.

  • Tags match the sibling tasks.
  • The task_id follows skill-agent-guardrail-coded-*.
  • run_command grades the outcome, not the agent's own report.
  • Prompts are minimal.
  • The reference.directory change to ../../.. in user_prompt_attacks_decorator.yaml matches the new import of _shared.
  • The new shared module has a CODEOWNERS entry.
  • Low: check_user_prompt_attacks_decorator.py uses upa[0]. If an agent adds two UPA decorators (one on a tool, one on the factory), the result depends on AST walk order. Prefer the first one that passes the factory check.

9. SKILL.md, file-attachments.md, low-code files-guide.md availability row

OK. One-line routing pointers. Frontmatter is unchanged.

What's Missing

  • Nothing blocking. The follow-up the PR already lists (a file_tool_judge fixture that does not pre-render the file, so the task actually discriminates) would make the new e2e task more than a regression guard.

Area Ratings

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_block and DetectionScope are exported from uipath_langchain.guardrails.
    • UiPathDeterministicGuardrailMiddleware(detection_scope=...) raises TypeError.
    • Rule 4: an invalid string raises ValueError at import.
    • sdk.guardrails.evaluate_guardrail(input_data=..., guardrail=BuiltInValidatorGuardrail(...)) uses those constructor kwargs.
    • upload_async(name=, source_path=) is the signature.
  • Whether UiPathPromptInjectionMiddleware also accepts detection_scope. Rule 1 of files-guide.md leaves 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 messages key 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.

@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

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

  • ✅ The author says all three tasks passed: "Ran skill-agent-guardrail-coded-pii-middleware locally and it passed. Ran skill-agent-guardrail-coded-user-prompt-attacks-decorator locally and it passed. Ran skill-agent-guardrail-coded-file-tool-judge locally and it passed."

Per-task lint

tests/tasks/uipath-agents/coded/guardrails/file_tool_judge/file_tool_judge.yaml — verdict: Low

Issues:

  • [Low] Meaningful coverage: the ContractIntakeCodedAgent fixture already declares the Attachment and renders it into the user message (L4-6). So require_attachment_input / require_rendered_attachments in check_file_tool_judge.py:150-151 only prove the agent didn't break the fixture. They don't prove the agent wired the file. The PR body already says this is a regression guard.

Suggested fixes:

  • Follow-up already planned in the PR body: a fixture variant without the pre-rendered Attachment, so the render check becomes something the agent has to get right.

tests/tasks/uipath-agents/coded/guardrails/pii_middleware/pii_middleware.yaml — verdict: Low

Issues:

  • [Low] Meaningful coverage: run_command is the only criterion (L35-42). There is no skill_triggered check, unlike its siblings user_prompt_attacks_decorator.yaml:43 and user_prompt_attacks_middleware.yaml:40. A pass can't be attributed to the skill.

Suggested fixes:

  • Add - type: skill_triggered with skill_name: "uipath-agents", expected_skill: "uipath-agents", weight: 1.0, pass_threshold: 1.0 ahead of the run_command.

tests/tasks/uipath-agents/coded/guardrails/user_prompt_attacks_decorator/user_prompt_attacks_decorator.yaml — verdict: OK (theme-captured; see Theme 1)

Within-PR duplicates

  • No duplicate clusters detected. Only one task is new (file_tool_judge). Its nearest neighbour, llm_as_judge.yaml, tests something materially different: Agent-scope middleware on a text-only fixture, against a Tool-target judge with detection_scope=Files at PRE.

Themes

  • Theme 1 [Low] — e2e tier, but grading is static only. All 3 tasks are tagged e2e, yet the checkers grade graph.py by parsing it (AST). Nothing confirms the generated file imports against the installed uipath-langchain. The PR body says passing variants were import-checked by hand, but CI doesn't repeat that check. A wrong import uipath_langchain.guardrails symbol, or a misnamed GuardrailExecutionStage/DetectionScope member, would still pass. Suggested: add a second run_command such as uv run python -c "import graph" (or python3 -I -c with the sandbox venv), at weight 2.0–3.0. Or retag the tasks integration if the tasks are meant to check structure only.

CLI verb reachability: scripts/check-cli-verbs.py couldn't run in this job, because the bash command was not permitted. I checked by hand instead: uip agent guardrails llm-as-judge-models (file_tool_judge.yaml:50) is in assets/uip-catalog-snapshot.json. None of the tasks has a run-limit field under agent:, and none pins @uipath/cli in env_packages.

Conclusion

  • ⚠ 2 tasks have issues; the highest severity is Low. Advisory only, not blocking merge.

apetraru-uipath and others added 2 commits November 5, 2026 19:39
…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>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant