Add Claude Code AI Agent step with CLI harness and Linux user impersonation#1996
Add Claude Code AI Agent step with CLI harness and Linux user impersonation#1996zentron wants to merge 23 commits into
Conversation
️✅ There are no secrets present in this pull request anymore.If these secrets were true positive and are still valid, we highly recommend you to revoke them. 🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request. |
Introduce provider selection (Anthropic/OpenAI) via variables, add Microsoft.Extensions.AI.OpenAI bridge package, and bump MEAI packages to 10.5.0 for ModelContextProtocol 1.3.0 compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ce messages Introduce InvokeClaudeCodeBehaviour as an alternative to the SDK-based provider, shelling out to `claude -p` with stream-json output. Includes typed stream event models, a dedicated stream processor, temp working directory with skills support, and an ai-agent-usage service message for reporting cost/token metrics back to the server. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add --strict-mcp-config with configurable MCP servers passed via ClaudeCodeOptions. Wire up GitHub and Octopus MCP servers from variables in InvokeClaudeCodeBehaviour. Introduce enums for StreamEventType and ContentBlockType used in processor routing, but keep model properties as strings to avoid JsonSerializer throwing on unknown values from the evolving CLI format. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ProcessCredentials and RunAs support to ClaudeCodeCliRunner. Windows uses ProcessStartInfo.UserName/PasswordInClearText natively. Linux wraps in sudo -u <user> -- env ANTHROPIC_API_KEY=<key> claude <args> to avoid -E requiring SETENV in sudoers (see ADR-001). Add unit tests for stream processor (17 tests) and CLI runner (10 tests). Replace integration test fixture with clean validation and Claude Code tests. Fix malformed JSON handling and result event fallback in stream processor. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…uilder Replace hardcoded GitHubToken MCP config with a generic JSON-encoded McpServers variable that supports user-configured MCP servers. The Octopus MCP server remains hardcoded. Add McpServerEntry record for deserialization. Update stream models with full init/hook/result fields from Claude Code stream-json output. Extract CLI argument construction into ClaudeCommandArgsBuilder with max-turns and max-budget-usd support. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…d fixes Write filtered deployment variables to deployment-variables.json in the working directory so the agent can read deployment context on demand. Add --effort flag support (low/medium/high/xhigh/max). Auto-add mcp__<server>__* to --allowedTools for all configured MCP servers so they aren't blocked by --permission-mode dontAsk. Add --bare flag for clean environment isolation. Fix verbose log missing newlines between JSON events. Update PermissionDenial model to structured record. Remove superpowers planning docs from repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a customEnvVars dictionary for env vars that need to be explicitly set on the process (currently just ANTHROPIC_API_KEY). No behaviour change — prepares for passing these vars into ApplyCredentials so they can be inlined into a su -c command on Linux in a follow-up task. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a static ShellQuote method to ClaudeCodeCliRunner that wraps a value in single quotes and escapes any embedded single quotes as '\''. This is needed to safely embed values (API keys, env var values) into su -c command strings without shell injection risk. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tion
On Linux, instead of using ProcessStartInfo.UserName (which requires CAP_SETUID),
wrap the command with `script -qec "su - {user} -c '{envVars} {cmd}'" /dev/null`
to allocate a pseudo-TTY for su. Custom env vars are inlined into the command
string since `su -` starts a login shell that clears the environment.
Windows behaviour is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ate ADR comment Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
5313cf9 to
c344557
Compare
eddymoulton
left a comment
There was a problem hiding this comment.
Comments to discuss - we didn't look at the tests
| var username = runAs?.Username!; | ||
| if (runAs == null || string.IsNullOrEmpty(username)) | ||
| { | ||
| var startInfo1 = StartSimpleProcess(workingDir, argsBuilder, environmentVariables); | ||
| var process1 = Process.Start(startInfo1)!; | ||
| return process1; | ||
| } |
There was a problem hiding this comment.
Should StartMacOrLinuxProcess just be this (ie. remove runAs support) for this PR?
Then when it's confirmed working we can add the functionality in and have it tested.
There was a problem hiding this comment.
given the whole thing is feature flagged and still in development im not too concerned about this one at the moment.
| runAs, | ||
| argsBuilder, | ||
| environmentVariables, | ||
| ct); |
There was a problem hiding this comment.
Have looked into separate classes for windows and mac/linux instead of this if (platform) here?
There was a problem hiding this comment.
I did at one point, but felt simple enough to just combine into one. Open to using OO if we add enough additional complexity (or improves testing) that we think warrants.
|
|
||
| foreach (var skill in userSkills) | ||
| { | ||
| var dirName = SanitizeFileName(skill.Name); |
There was a problem hiding this comment.
Should we put these skills in directories that aren't named by the skill name to avoid a lot of this sanitization work?
Requires Server Change
Background
Adds the Calamari side of the Claude Code Step — a new step type that runs Anthropic's Claude Code CLI as a non-interactive agent during deployments and runbooks. The step streams structured JSON output back to the task log, supports user-configured MCP servers and custom skills, and runs under an impersonated user context for isolation.
Results
flowchart TD A[RunAgentCommand] --> B[Setup working directory<br/>skills, MCP config, system prompt] B --> C{Platform?} C -->|Windows| D[Native ProcessStartInfo<br/>user impersonation] C -->|Linux / macOS| E["Wrapper script + script(1) / su(1)<br/>password via stdin"] D --> F[claude CLI — stream-json mode] E --> F F --> G[Stream processor<br/>task log, service messages, artifacts]Calamari.AiAgentproject withRunAgentCommandentry pointscript(1)+su(1)with a wrapper shell script to avoid nested escaping. Windows uses nativeProcessStartInfo.UserName.build.shfix: replacedperlJSON parser withgrep/sed— Amazon Linux 2 container droppedperlResolves
How to review
ClaudeCodeProcessStartInfo.cs— the most nuanced file. Handles the platform split andscript/suwrapper script approach. macOS and Linuxscriptcommands have different argument ordering.ClaudeCodeStreamProcessor.cs+ stream models — JSON deserialization of Claude'sstream-jsonoutput.ClaudeCodeCliRunner.cs— orchestrator that wires up the process, streams stdout/stderr, and collects artifacts.ClaudeCommandArgsBuilderare straightforward — skim these.Caveats
script/suapproach reads aPassword:prompt from stdout before forwarding to the stream processor — fragile ifsuchanges its prompt format. Acceptable for POC.