feat: add skills feature to advanced agent with workspace to load skills#977
feat: add skills feature to advanced agent with workspace to load skills#977mariadhakalUipath wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
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] | Noneparameter tocreate_advanced_agent/create_advanced_agent_graphand 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. |
| memory=list(memory) or None, | ||
| skills=list(skills) if skills is not None else None, | ||
| ) |
| 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( |
| 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, | ||
| ) |
| 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 |
| 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() |
e5d2b48 to
5cee6e8
Compare
| 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)}" | ||
| ) |
| 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, |
There was a problem hiding this comment.
because of the default None in the function call this could be simply skills = list(skills)
There was a problem hiding this comment.
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>
5cee6e8 to
528a307
Compare
|



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.