mirror of
https://github.com/langchain-ai/deepagents.git
synced 2026-07-21 09:15:22 -04:00
37c5be9561
## Summary Pin `actions/checkout@v6` → `actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1` in `deepagents-example.yml.example`. All other workflows and composite actions in the repo were already SHA-pinned. This completes coverage for every third-party action reference in `.github/`. ## Why SHA-pinning third-party GitHub Actions is a supply-chain security best practice recommended by GitHub and security scanners. Tag-based references (`@v6`) are mutable — a compromised maintainer account or stolen token can push malicious code under the same tag. SHA-pinned references are immutable. ## Notes The `langchain-ai/deepagents@main` self-reference in the example template is intentionally left unpinned — it is a template file (`.example` suffix) for downstream consumers, not a live workflow. Co-authored-by: Deep Agent <agent@deepagents.dev>
219 lines
9.9 KiB
Plaintext
219 lines
9.9 KiB
Plaintext
name: Deep Agents Example
|
|
|
|
on:
|
|
issue_comment:
|
|
types: [created]
|
|
pull_request_review_comment:
|
|
types: [created]
|
|
workflow_dispatch:
|
|
inputs:
|
|
prompt:
|
|
description: "Prompt for the agent"
|
|
required: true
|
|
|
|
# Cancel superseded runs when @deepagents is mentioned multiple times on the same PR/issue
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number || github.run_id }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
deepagents:
|
|
if: |
|
|
github.event_name == 'workflow_dispatch' ||
|
|
(
|
|
contains(github.event.comment.body, '@deepagents') &&
|
|
(
|
|
github.event.comment.author_association == 'OWNER' ||
|
|
github.event.comment.author_association == 'MEMBER' ||
|
|
github.event.comment.author_association == 'COLLABORATOR'
|
|
) &&
|
|
(
|
|
github.event_name == 'pull_request_review_comment' ||
|
|
github.event.issue.pull_request
|
|
)
|
|
)
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: write
|
|
issues: write
|
|
pull-requests: write
|
|
|
|
steps:
|
|
- name: Resolve PR number
|
|
if: github.event_name != 'workflow_dispatch'
|
|
id: pr-info
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
# issue_comment uses event.issue.number; review_comment uses event.pull_request.number
|
|
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
|
run: |
|
|
echo "number=$PR_NUMBER" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Acknowledge trigger
|
|
if: github.event_name != 'workflow_dispatch'
|
|
continue-on-error: true
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
COMMENT_ID: ${{ github.event.comment.id }}
|
|
EVENT_NAME: ${{ github.event_name }}
|
|
REPO: ${{ github.repository }}
|
|
run: |
|
|
# issue_comment reactions use issues/comments; review_comment uses pulls/comments
|
|
if [ "$EVENT_NAME" = "pull_request_review_comment" ]; then
|
|
API_PATH="repos/${REPO}/pulls/comments/${COMMENT_ID}/reactions"
|
|
else
|
|
API_PATH="repos/${REPO}/issues/comments/${COMMENT_ID}/reactions"
|
|
fi
|
|
if ! gh api --method POST "$API_PATH" -f content='rocket'; then
|
|
echo "::warning::Failed to add reaction to comment ${COMMENT_ID} — comment may have been deleted or token may lack permissions"
|
|
fi
|
|
|
|
- name: Get PR head SHA
|
|
if: github.event_name != 'workflow_dispatch'
|
|
id: pr-sha
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
PR_NUMBER: ${{ steps.pr-info.outputs.number }}
|
|
run: |
|
|
PR_DATA=$(gh pr view "$PR_NUMBER" --repo "$GITHUB_REPOSITORY" --json headRefOid,headRefName)
|
|
PR_SHA=$(echo "$PR_DATA" | jq -r '.headRefOid')
|
|
PR_BRANCH=$(echo "$PR_DATA" | jq -r '.headRefName')
|
|
|
|
if [ -z "$PR_SHA" ] || [ "$PR_SHA" = "null" ] || [ -z "$PR_BRANCH" ] || [ "$PR_BRANCH" = "null" ]; then
|
|
echo "::error::Failed to resolve PR head for #${PR_NUMBER}. API response: ${PR_DATA}"
|
|
exit 1
|
|
fi
|
|
|
|
echo "sha=$PR_SHA" >> "$GITHUB_OUTPUT"
|
|
echo "branch=$PR_BRANCH" >> "$GITHUB_OUTPUT"
|
|
|
|
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
with:
|
|
# Use the PR branch name so the agent can commit and push to the PR directly.
|
|
ref: ${{ github.event_name != 'workflow_dispatch' && steps.pr-sha.outputs.branch || '' }}
|
|
|
|
- name: Build PR context prompt
|
|
if: github.event_name != 'workflow_dispatch'
|
|
id: build-prompt
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
TRIGGER_COMMENT_BODY: ${{ github.event.comment.body }}
|
|
TRIGGER_COMMENT_AUTHOR: ${{ github.event.comment.user.login }}
|
|
PR_NUMBER: ${{ steps.pr-info.outputs.number }}
|
|
run: |
|
|
PROMPT_FILE=$(mktemp)
|
|
GH_STDERR=$(mktemp)
|
|
trap 'rm -f "$PROMPT_FILE" "$GH_STDERR"' EXIT
|
|
|
|
# Fetch PR data
|
|
if ! PR_DATA=$(gh pr view "$PR_NUMBER" --json title,body,author,state,headRefName,baseRefName 2>"$GH_STDERR"); then
|
|
echo "::error::Failed to fetch PR #${PR_NUMBER} data: $(cat "$GH_STDERR"). Check that the PR exists and the token has 'pull-requests: read' permission."
|
|
exit 1
|
|
fi
|
|
|
|
PR_TITLE=$(echo "$PR_DATA" | jq -r '.title // "Untitled"')
|
|
PR_BODY=$(echo "$PR_DATA" | jq -r '.body // "No description"')
|
|
PR_AUTHOR=$(echo "$PR_DATA" | jq -r '.author.login // "unknown"')
|
|
PR_STATE=$(echo "$PR_DATA" | jq -r '.state // "unknown"')
|
|
PR_HEAD=$(echo "$PR_DATA" | jq -r '.headRefName // "unknown"')
|
|
PR_BASE=$(echo "$PR_DATA" | jq -r '.baseRefName // "unknown"')
|
|
|
|
# Fetch PR diff stats (first page)
|
|
if ! DIFF_STAT=$(gh pr diff "$PR_NUMBER" --name-only 2>"$GH_STDERR"); then
|
|
echo "::warning::Failed to fetch PR diff: $(cat "$GH_STDERR")"
|
|
DIFF_STAT="[Error fetching diff — see workflow logs]"
|
|
fi
|
|
|
|
# Fetch PR comments (first 20 — older ones omitted)
|
|
if ! PR_COMMENTS=$(gh api "repos/$GITHUB_REPOSITORY/issues/$PR_NUMBER/comments?per_page=20" \
|
|
--jq '.[] | "<comment author=\"\(.user.login)\">\(.body)</comment>"' 2>"$GH_STDERR"); then
|
|
echo "::warning::Failed to fetch PR comments: $(cat "$GH_STDERR")"
|
|
PR_COMMENTS="[Error fetching comments — see workflow logs]"
|
|
fi
|
|
|
|
# Fetch PR reviews (first 10)
|
|
if ! PR_REVIEWS=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews?per_page=10" \
|
|
--jq '.[] | "<review author=\"\(.user.login)\" state=\"\(.state)\">\(.body // "No review body")</review>"' 2>"$GH_STDERR"); then
|
|
echo "::warning::Failed to fetch PR reviews: $(cat "$GH_STDERR")"
|
|
PR_REVIEWS="[Error fetching reviews — see workflow logs]"
|
|
fi
|
|
|
|
# Fetch review comments / inline code comments (first 30)
|
|
if ! REVIEW_COMMENTS=$(gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments?per_page=30" \
|
|
--jq '.[] | "<review-comment author=\"\(.user.login)\" path=\"\(.path)\" line=\"\(.line // .original_line)\">\(.body)</review-comment>"' 2>"$GH_STDERR"); then
|
|
echo "::warning::Failed to fetch review comments: $(cat "$GH_STDERR")"
|
|
REVIEW_COMMENTS="[Error fetching review comments — see workflow logs]"
|
|
fi
|
|
|
|
cat > "$PROMPT_FILE" << 'PROMPT_HEADER'
|
|
<instructions>
|
|
The user has tagged @deepagents in a comment on this pull request. Your task is to resolve their request in the simplest way possible.
|
|
|
|
You have shell access with git and gh available. The repository is checked out on the PR branch.
|
|
|
|
Determine whether the comment requires code changes, and if so implement them directly.
|
|
- Make only the changes requested. Do not make unrelated changes.
|
|
- Do not leave comments in your code about the request or changes you're making.
|
|
- Keep changes minimal and focused.
|
|
|
|
If the comment does not require code changes (e.g. a question), respond by creating a comment on the PR with your answer.
|
|
|
|
After making changes, commit them to the current branch.
|
|
|
|
IMPORTANT: When you are finished, you MUST post a brief summary comment on the PR using `gh pr comment`. The comment should:
|
|
- Briefly describe what you did (1-3 sentences)
|
|
- List any files changed or commits made
|
|
- Note if you were unable to complete any part of the request
|
|
Always post this summary, even if the task was simple or no code changes were needed.
|
|
</instructions>
|
|
|
|
PROMPT_HEADER
|
|
|
|
# Write PR context using printf to avoid shell expansion of user-controlled content
|
|
{
|
|
printf '<pull-request>\n'
|
|
printf '<title>%s</title>\n' "$PR_TITLE"
|
|
printf '<author>%s</author>\n' "$PR_AUTHOR"
|
|
printf '<state>%s</state>\n' "$PR_STATE"
|
|
printf '<base>%s</base>\n' "$PR_BASE"
|
|
printf '<head>%s</head>\n' "$PR_HEAD"
|
|
printf '<body>\n%s\n</body>\n' "$PR_BODY"
|
|
printf '</pull-request>\n\n'
|
|
|
|
printf '<changed-files>\n%s\n</changed-files>\n\n' "$DIFF_STAT"
|
|
printf '<pull-request-comments>\n%s\n</pull-request-comments>\n\n' "$PR_COMMENTS"
|
|
printf '<pull-request-reviews>\n%s\n</pull-request-reviews>\n\n' "$PR_REVIEWS"
|
|
printf '<review-comments>\n%s\n</review-comments>\n\n' "$REVIEW_COMMENTS"
|
|
|
|
printf '<trigger-comment>\n'
|
|
printf 'This is the comment that triggered this workflow. Focus on resolving this request.\n'
|
|
printf '<author>%s</author>\n' "$TRIGGER_COMMENT_AUTHOR"
|
|
printf '<body>\n%s\n</body>\n' "$TRIGGER_COMMENT_BODY"
|
|
printf '</trigger-comment>\n\n'
|
|
|
|
printf 'Given all of this context, resolve the trigger comment in the simplest way possible.\n'
|
|
printf 'IMPORTANT: The trigger comment takes precedence. Focus on what was asked, using the PR context to inform your approach.\n'
|
|
} >> "$PROMPT_FILE"
|
|
|
|
# Set output using heredoc with random delimiter
|
|
DELIMITER="PROMPT_$(openssl rand -hex 16)"
|
|
{
|
|
echo "prompt<<${DELIMITER}"
|
|
cat "$PROMPT_FILE"
|
|
echo "${DELIMITER}"
|
|
} >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Run Deep Agents
|
|
uses: langchain-ai/deepagents@main
|
|
with:
|
|
prompt: ${{ github.event_name != 'workflow_dispatch' && steps.build-prompt.outputs.prompt || github.event.inputs.prompt }}
|
|
model: claude-sonnet-4-6
|
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
# Or: openai_api_key: ${{ secrets.OPENAI_API_KEY }}
|
|
# Or: google_api_key: ${{ secrets.GOOGLE_API_KEY }}
|
|
skills_repo: langchain-ai/langchain-skills
|