Skip to content

feat: add skills feature to advanced agent with workspace to load skills#977

Open
mariadhakalUipath wants to merge 1 commit into
mainfrom
feat/uipath-skills-for-advanced-agents
Open

feat: add skills feature to advanced agent with workspace to load skills#977
mariadhakalUipath wants to merge 1 commit into
mainfrom
feat/uipath-skills-for-advanced-agents

Conversation

@mariadhakalUipath

@mariadhakalUipath mariadhakalUipath commented Jul 8, 2026

Copy link
Copy Markdown

Title: feat: add skills support to advanced agents with sandboxed workspace tools

Summary

Adds skills feature for advanced agents. Agent can act on it — run uip commands, review what it ran, and verify what it scaffolded.

Copilot AI review requested due to automatic review settings July 8, 2026 21:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds opt-in “skills” support to deepagents-backed advanced agents, including workspace-mounted skill sources and auto-injected sandboxed workspace tools (CLI runner, audit log reader, and scaffold verifier) when running on a FilesystemBackend.

Changes:

  • Adds a skills: Sequence[str] | None parameter to create_advanced_agent / create_advanced_agent_graph and wires it into deepagents’ SkillsMiddleware with workspace-source prepending/dedup.
  • Introduces three workspace-bound internal tools (uipath_cli, uipath_cli_usage, uipath_verify_files) plus timeout/audit/denylist and required-files validation logic.
  • Adds focused test coverage for skills opt-in behavior, tool injection gating, CLI sandboxing/audit behavior, usage reading, and file verification.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/uipath_langchain/agent/advanced/agent.py Wires skills into deepagents and injects workspace tools when skills are active on a filesystem backend.
src/uipath_langchain/agent/advanced/utils.py Defines skills workspace layout constants (SKILLS_DIR_NAME, SKILLS_VIRTUAL_PATH).
src/uipath_langchain/agent/advanced/init.py Exports skills-related constants from the advanced agent package.
src/uipath_langchain/agent/tools/internal_tools/uipath_cli_tool.py Implements the sandboxed CLI runner, audit-log usage reader, and required-files verifier tools.
src/uipath_langchain/agent/tools/internal_tools/init.py Exposes the new internal tool constructors/utilities from the internal tools package.
tests/agent/advanced/test_skills_injection.py Tests skills opt-in gating and workspace source prepending/dedup behavior.
tests/agent/advanced/test_cli_tool_injection.py Tests conditional injection of the three workspace tools based on skills + filesystem backend.
tests/agent/tools/internal_tools/test_uipath_cli_tool.py Tests CLI sandboxing: binary allowlist, subdir jailing, timeout resolution, denylist, and auditing.
tests/agent/tools/internal_tools/test_cli_usage_tool.py Tests reading and summarizing CLI audit log records (including malformed line handling).
tests/agent/tools/internal_tools/test_verify_files_tool.py Tests file verification behavior including glob patterns, unknown types, and workspace escape prevention.

Comment on lines 86 to 88
memory=list(memory) or None,
skills=list(skills) if skills is not None else None,
)
Comment on lines +152 to +155
workspace_path = Path(workspace).expanduser().resolve()
workspace_path.mkdir(parents=True, exist_ok=True)
audit_path = Path(audit_log or (workspace_path / AUDIT_LOG_FILENAME)).expanduser()
denied = frozenset(
Comment on lines +264 to +272
return finish(
"ok" if proc.returncode == 0 else "nonzero_exit",
f"exit_code: {proc.returncode}\n--- stdout ---\n{proc.stdout}\n--- stderr ---\n{proc.stderr}",
exit_code=proc.returncode,
stdout=proc.stdout[:MAX_LOG_BYTES],
stderr=proc.stderr[:MAX_LOG_BYTES],
stdout_truncated=len(proc.stdout) > MAX_LOG_BYTES,
stderr_truncated=len(proc.stderr) > MAX_LOG_BYTES,
)
Comment on lines +281 to +294
def _read_audit_records(audit_path: Path) -> list[dict[str, Any]]:
"""Return audit records of cli commands execution in invocation order; empty if the log is absent."""
if not audit_path.is_file():
return []
records: list[dict[str, Any]] = []
for raw in audit_path.read_text(encoding="utf-8").splitlines():
line = raw.strip()
if not line:
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError:
continue
return records
Comment on lines +331 to +335
def _file_matches(project_dir: Path, pattern: str) -> bool:
"""True if ``pattern`` (literal name or glob) matches a file in the dir root."""
if any(ch in pattern for ch in "*?["):
return any(project_dir.glob(pattern))
return (project_dir / pattern).is_file()
@mariadhakalUipath mariadhakalUipath force-pushed the feat/uipath-skills-for-advanced-agents branch 2 times, most recently from e5d2b48 to 5cee6e8 Compare July 10, 2026 03:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment on lines +118 to +123
binary = os.path.basename(argv[0])
if binary not in ALLOWED_BINARIES:
return (
f"ERROR: '{binary}' is not allowed. Only these binaries are "
f"permitted: {sorted(ALLOWED_BINARIES)}"
)
Comment on lines +59 to +60
memory=list(memory) or None,
skills=list(skills) if skills is not None else None,
backend=backend,
response_format=response_format,
memory=list(memory) or None,
skills=list(skills) if skills is not None else None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because of the default None in the function call this could be simply skills = list(skills)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed skills = list(skills) or None instead to avoid triggering deepagents skillsmidleware when the skills list is passed empty.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mariadhakalUipath mariadhakalUipath force-pushed the feat/uipath-skills-for-advanced-agents branch from 5cee6e8 to 528a307 Compare July 10, 2026 19:37
@sonarqubecloud

Copy link
Copy Markdown

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.

3 participants