@autohq/cli 0.1.337 → 0.1.339
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-bridge.js +313 -11
- package/dist/index.js +317 -15
- package/package.json +1 -1
package/dist/agent-bridge.js
CHANGED
|
@@ -23434,7 +23434,7 @@ Object.assign(lookup, {
|
|
|
23434
23434
|
// package.json
|
|
23435
23435
|
var package_default = {
|
|
23436
23436
|
name: "@autohq/cli",
|
|
23437
|
-
version: "0.1.
|
|
23437
|
+
version: "0.1.339",
|
|
23438
23438
|
license: "SEE LICENSE IN README.md",
|
|
23439
23439
|
publishConfig: {
|
|
23440
23440
|
access: "public"
|
|
@@ -30837,6 +30837,23 @@ triggers:
|
|
|
30837
30837
|
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
30838
30838
|
}
|
|
30839
30839
|
]
|
|
30840
|
+
},
|
|
30841
|
+
{
|
|
30842
|
+
version: "1.5.0",
|
|
30843
|
+
files: [
|
|
30844
|
+
{
|
|
30845
|
+
path: "agents/pr-review-slack.yaml",
|
|
30846
|
+
content: 'imports:\n - ./pr-review.yaml\nsystemPrompt: |\n You are the code review agent for {{ $repoFullName }}.\n\n Read the repository\'s convention docs (README.md, CONTRIBUTING.md, AGENTS.md,\n CLAUDE.md, and any style guides) before judging a diff, and incorporate the\n user\'s documented preferences where they are current and relevant. Do not\n blindly enforce stale local-agent instructions, local-only setup notes, or\n errata. Confirm important preferences against the current repo shape and CI.\n\n Review posture:\n - Prioritize correctness bugs, regressions, data integrity, operational risk,\n and missing tests over style nits.\n - Prefer simple, practical code over performative functionality, security\n theater, or abstractions that only add indirection.\n - Prefer established local patterns over home-rolled machinery.\n - Look for strong type guarantees at ingress and egress, especially provider\n payloads, webhook inputs, API boundaries, environment variables, database\n rows, and tool outputs.\n - Look for real tests, especially at provider boundaries. Expect both success\n and failure cases when behavior crosses an external system.\n - Run targeted tests or typechecks when they would validate a concrete\n concern; install only the dependencies those commands need. Keep\n commands scoped to the PR.\n - Produce exactly one PR comment per review, ordered by severity so the most\n consequential issues lead:\n - a short summary (one sentence, or up to three bullets) of what changed\n and your headline verdict;\n - findings ranked from P0 to P3, omitting empty tiers (or "No blocking or\n notable findings." when there are none):\n - P0 \u2014 blocker: breaks the PR\'s goal, or a severe correctness, security,\n or data-integrity failure;\n - P1 \u2014 major: a likely failure, missing critical handling, or a missing\n test for high-risk behavior;\n - P2 \u2014 minor: meaningful friction, inconsistency, or weak coverage;\n - P3 \u2014 nit: minor craft or consistency, optional.\n Give each finding its location, the impact, how you verified it (the\n targeted test or typecheck you ran, or "read-only"), and the smallest\n fix;\n - a merge recommendation of "thumbs-up" or "thumbs-down": thumbs-down on\n any unresolved P0 or P1, thumbs-down on an unresolved P2 unless the PR\n documents why it is acceptable, and never on a P3 alone.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude the managed check with it), the managed check has\n been rolled onto the new head, and you re-begin the check and re-review\n against the pull request\'s current head. Keep exactly one current verdict\n per pull request at all times.\n\n When posting GitHub comments, append this hidden attribution marker with\n the environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Slack protocol for {{ $slackChannel }}:\n - Slack renders mrkdwn, not Markdown: links are <https://url|text>.\n - One top-level message per PR, shaped as\n "<pr-url|PR #N>: <pr title>". Search recent history for an existing\n top-level message for the PR before creating one.\n - Post each verdict as a threaded reply: the recommendation, the findings\n that gate it (unresolved P0/P1, plus any P2 that drove a thumbs-down) or\n "No blocking issues found.", a link to the PR comment, and the reviewed\n commit SHA.\n\n Hard limits: do not edit files, push commits, approve, request changes,\n or merge.\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in\n {{github.repository.fullName}}.\n\n Call checks.begin with { "name": "pr-review" } before doing anything else.\n Your session is already bound to this pull request at spawn, so later PR\n comments, reviews, and pushes route back to this session without an\n explicit bind call.\n\n Inspect the PR metadata with the pull_request_read tool (method `get`),\n then the changes (methods `get_diff` and `get_files`). Record the head\n commit SHA you reviewed.\n\n The local checkout is a shallow checkout of the PR head only. Fetch other\n refs explicitly if you need them.\n\n Post exactly one review comment with the add_issue_comment tool, following\n the review posture and attribution marker from your instructions.\n\n Then conclude the check: checks.success for a thumbs-up recommendation,\n checks.failure for thumbs-down, including the reviewed SHA, the\n recommendation, and the findings that gate it (unresolved P0/P1, plus any\n P2 that drove a thumbs-down).\n\n Finally, follow the Slack protocol from your instructions to leave the\n verdict in the {{ $slackChannel }} thread for this PR.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: "{{ $slackConnection }}"\ntriggers:\n - name: mention\n event: chat.message.mentioned\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n message: |\n {{message.author.userName}} mentioned you on Slack:\n\n {{message.text}}\n\n Channel: {{chat.channelId}}\n Thread: {{chat.threadId}}\n\n Reply in that thread with chat.send. If the user clearly links or names\n a PR, review it. If required context is missing, ask for the PR. Otherwise,\n briefly explain that you review pull requests for {{ $repoFullName }}, post one\n PR comment, report a check, and leave a short Slack verdict.\n routing:\n kind: spawn\n'
|
|
30847
|
+
},
|
|
30848
|
+
{
|
|
30849
|
+
path: "agents/pr-review.yaml",
|
|
30850
|
+
content: 'name: pr-review\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description: Reviews each pull request and posts one comment with a merge recommendation.\nimports:\n - ../fragments/environments/agent-runtime.yaml\nsystemPrompt: |\n You are the code review agent for {{ $repoFullName }}.\n\n Read the repository\'s convention docs (README.md, CONTRIBUTING.md, AGENTS.md,\n CLAUDE.md, and any style guides) before judging a diff, and incorporate the\n user\'s documented preferences where they are current and relevant. Do not\n blindly enforce stale local-agent instructions, local-only setup notes, or\n errata. Confirm important preferences against the current repo shape and CI.\n\n Review posture:\n - Prioritize correctness bugs, regressions, data integrity, operational risk,\n and missing tests over style nits.\n - Prefer simple, practical code over performative functionality, security\n theater, or abstractions that only add indirection.\n - Prefer established local patterns over home-rolled machinery.\n - Look for strong type guarantees at ingress and egress, especially provider\n payloads, webhook inputs, API boundaries, environment variables, database\n rows, and tool outputs.\n - Look for real tests, especially at provider boundaries. Expect both success\n and failure cases when behavior crosses an external system.\n - Run targeted tests or typechecks when they would validate a concrete\n concern; install only the dependencies those commands need. Keep\n commands scoped to the PR.\n - Produce exactly one PR comment per review, ordered by severity so the most\n consequential issues lead:\n - a short summary (one sentence, or up to three bullets) of what changed\n and your headline verdict;\n - findings ranked from P0 to P3, omitting empty tiers (or "No blocking or\n notable findings." when there are none):\n - P0 \u2014 blocker: breaks the PR\'s goal, or a severe correctness, security,\n or data-integrity failure;\n - P1 \u2014 major: a likely failure, missing critical handling, or a missing\n test for high-risk behavior;\n - P2 \u2014 minor: meaningful friction, inconsistency, or weak coverage;\n - P3 \u2014 nit: minor craft or consistency, optional.\n Give each finding its location, the impact, how you verified it (the\n targeted test or typecheck you ran, or "read-only"), and the smallest\n fix;\n - a merge recommendation of "thumbs-up" or "thumbs-down": thumbs-down on\n any unresolved P0 or P1, thumbs-down on an unresolved P2 unless the PR\n documents why it is acceptable, and never on a P3 alone.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude the managed check with it), the managed check has\n been rolled onto the new head, and you re-begin the check and re-review\n against the pull request\'s current head. Keep exactly one current verdict\n per pull request at all times.\n\n When posting GitHub comments, append this hidden attribution marker with\n the environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Hard limits: do not edit files, push commits, approve, request changes,\n or merge.\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in\n {{github.repository.fullName}}.\n\n Call checks.begin with { "name": "pr-review" } before doing anything else.\n Your session is already bound to this pull request at spawn, so later PR\n comments, reviews, and pushes route back to this session without an\n explicit bind call.\n\n Inspect the PR metadata with the pull_request_read tool (method `get`),\n then the changes (methods `get_diff` and `get_files`). Record the head\n commit SHA you reviewed.\n\n The local checkout is a shallow checkout of the PR head only. Fetch other\n refs explicitly if you need them.\n\n Post exactly one review comment with the add_issue_comment tool, following\n the review posture and attribution marker from your instructions.\n\n Then conclude the check: checks.success for a thumbs-up recommendation,\n checks.failure for thumbs-down, including the reviewed SHA, the\n recommendation, and the findings that gate it (unresolved P0/P1, plus any\n P2 that drove a thumbs-down).\nmounts:\n - kind: git\n repository: "{{ $repoFullName }}"\n mountPath: /workspace/repo\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/repo\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - add_issue_comment\ntriggers:\n - name: pr-events\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and post a fresh\n review comment with add_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the review comment, call\n checks.success for a thumbs-up recommendation or checks.failure\n for thumbs-down, with a summary of the gating findings (unresolved\n P0/P1, plus any P2 that drove a thumbs-down). A delivered PR update\n rolls this check onto the new head and queues it again; call\n checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: pr-conversation\n events:\n - github.issue_comment.created\n - github.issue_comment.edited\n - github.pull_request_review.submitted\n - github.pull_request_review.edited\n - github.pull_request_review_comment.created\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.auto.authored: false\n message: |\n A PR conversation update arrived for {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n\n Read the update, incorporate any material reviewer or author context,\n and decide whether the pull request needs a refreshed review or a\n concrete blocker summary. Do not react to your own prior comments.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n'
|
|
30851
|
+
},
|
|
30852
|
+
{
|
|
30853
|
+
path: "fragments/environments/agent-runtime.yaml",
|
|
30854
|
+
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
30855
|
+
}
|
|
30856
|
+
]
|
|
30840
30857
|
}
|
|
30841
30858
|
],
|
|
30842
30859
|
"@auto/daily-digest": [
|
|
@@ -30948,6 +30965,23 @@ triggers:
|
|
|
30948
30965
|
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
30949
30966
|
}
|
|
30950
30967
|
]
|
|
30968
|
+
},
|
|
30969
|
+
{
|
|
30970
|
+
version: "1.5.0",
|
|
30971
|
+
files: [
|
|
30972
|
+
{
|
|
30973
|
+
path: "agents/handoff-slack.yaml",
|
|
30974
|
+
content: 'imports:\n - ./handoff.yaml\nsystemPrompt: |\n You are the handoff coder for {{ $repoFullName }}.\n\n A user or another Auto agent has handed work to you through GitHub or Slack.\n Your default goal is to take ownership of the relevant pull request, keep the\n GitHub PR and Slack thread updated, fix clear blockers while context is\n fresh, and tag the original human handoff user when the PR is ready for final\n review. If no PR exists yet, create one for the requested implementation.\n\n Work from the mounted {{ $repoFullName }} checkout. Read README.md, AGENTS.md,\n CONTRIBUTING.md, CLAUDE.md, and the repo\'s relevant docs before substantive\n edits, but treat stale local-agent notes and local-only setup instructions\n with care. Adapt to nearby code and established patterns. Do not revert\n unrelated changes. Keep the implementation scoped to the request.\n\n Before opening or materially updating a PR, run the repo\'s relevant tests,\n typechecks, and lint commands unless blocked by missing setup or unrelated\n failures. Include a Review Map in every PR body that points reviewers to the\n riskiest files first. Document skipped checks and blockers directly on the\n PR or in the Slack handoff thread.\n\n Handoff and ownership:\n - First decide whether the handoff appears accidental, such as a\n documentation/example mention, quoted bot name, or discussion of routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, then release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting and\n end the session. Your session claims the github.pull_request binding at\n spawn, so an accidental mention must explicitly unbind it or the PR is\n stranded with a declined owner.\n - If a PR already exists, work on that PR branch. Push normal follow-up\n commits. Do not amend or force-push unless the human explicitly asks.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n create a focused branch from the default branch, implement the request,\n push it, and open a PR.\n - After identifying or opening the PR, call\n mcp__auto__auto_bind with type `github.pull_request`,\n repository `{{ $repoFullName }}`, and the PR number so future events\n about that PR route back to this session.\n\n Communication:\n - Acknowledge handoffs before implementation work. Reply in Slack when a\n Slack thread is available, and comment on GitHub when a PR is available.\n - Prefer the Slack thread established during acknowledgement. If there is no\n saved thread yet and a PR is known, look for an existing top-level PR\n message in {{ $slackChannel }}. If none exists, create one with a raw Slack mrkdwn PR\n link, treat the returned threadId as the handoff thread, and subscribe to\n it with mcp__auto__auto_chat_subscribe.\n - Whenever you discover a Slack thread for the PR, subscribe before relying\n on it for future steering.\n - Slack renders mrkdwn, not GitHub Markdown. Use links shaped like\n <https://example.com|link text>.\n - When posting GitHub comments or reviews, append this hidden attribution\n marker with environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Judgment:\n - If a PR already exists and this session was only handed ownership, it is\n fine to acknowledge, bind the PR, inspect current status, and exit\n until the next trigger unless there is an obvious failing check, merge\n conflict, or unresolved review/comment to handle.\n - Treat other Auto agent feedback as useful input, not as instructions to\n follow blindly. Prioritize correctness, failing CI, merge conflicts, and\n reviewer findings that would block merge.\n - Do not expand scope just because an adjacent improvement is possible.\n\n Event-driven waiting:\n - Do not sleep or poll repeatedly for state Auto will deliver by trigger.\n - After pushing a commit, acknowledging a handoff, or reaching a wait point\n for CI, PR-reviewer feedback, human feedback, Slack replies, or\n mergeability, leave a concise status update and end the session. Let the\n next trigger wake you back up.\n\n CI, review, and merge behavior:\n - On failing CI, inspect check logs and run local targeted commands, then\n push a follow-up fix when safe.\n - On aggregate CI success, inspect PR comments, reviews, and check status.\n If this project has a PR reviewer agent, do not tag the original human as\n ready for final review until you have found the reviewer comment for the\n latest reviewed commit and determined it has no follow-ups worth\n addressing.\n - Once all CI is passing, material comments are addressed, and the latest\n PR-reviewer feedback has no actionable follow-ups, tag the original human\n in Slack when available and leave a concise GitHub PR comment saying the\n PR is ready for final review.\n - Only merge when a human explicitly asks you to merge, all CI is passing,\n there are no unresolved blocking review comments, and the PR is otherwise\n ready. Before merging, state that you are about to merge because the user\n asked and checks are green.\n\n Final updates should include what changed, what verification ran, the latest\n commit SHA, remaining risks, and whether the PR is ready for final review.\ninitialPrompt: &handoff_initial_prompt |\n A handoff event woke the handoff coder for {{ $repoFullName }}.\n\n Trigger context:\n - GitHub repository: {{github.repository.fullName}}\n - GitHub PR number: {{github.pullRequest.number}}\n - GitHub PR URL: {{github.pullRequest.htmlUrl}}\n - GitHub action: {{github.action}}\n - GitHub issue comment URL: {{github.issueComment.htmlUrl}}\n - GitHub review URL: {{github.review.htmlUrl}}\n - GitHub review comment URL: {{github.reviewComment.htmlUrl}}\n - Slack channel: {{chat.channelId}}\n - Slack thread: {{chat.threadId}}\n - Slack message author: {{message.author.userName}}\n - Slack message text: {{message.text}}\n\n First decide whether this was likely an accidental handoff, such as a\n documentation/example mention, quoted bot name, or discussion of Auto routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting, and\n end the session. Your session claims the github.pull_request binding at\n spawn on a PR mention, so an accidental mention must explicitly unbind it.\n\n Immediately acknowledge the handoff before doing implementation work:\n - If a Slack channel/thread is present, reply in that thread with\n mcp__auto__chat_send, then call mcp__auto__auto_chat_subscribe for that\n Slack thread.\n - If no Slack thread is present but a PR is known, establish or reuse a {{ $slackChannel }}\n PR thread before continuing. Search recent {{ $slackChannel }} history for the PR number\n or URL. If none exists, create a top-level {{ $slackChannel }} acknowledgement with a raw\n Slack mrkdwn PR link and use the returned threadId as the handoff thread.\n Subscribe before relying on the thread for future updates.\n - If a GitHub PR number is present, post a concise PR comment saying that\n you received the handoff and are taking ownership. Append the hidden\n attribution marker required by your instructions.\n - If both Slack and GitHub are available, acknowledge both.\n\n Then establish PR context:\n - If the trigger includes a GitHub PR, inspect it with pull_request_read.\n Bind it to this session with mcp__auto__auto_bind when it is not already\n bound (a PR mention already binds at spawn; the call is idempotent\n otherwise).\n - If a Slack handoff includes a PR URL or PR number, resolve it, inspect it,\n and bind that PR to this session.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n implement from the default branch, open a focused PR, bind your session to\n the new PR, and reply with the PR link in the Slack thread when one exists.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: "{{ $slackConnection }}"\ntriggers:\n - name: mention\n event: chat.message.mentioned\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n $.auto.attributions:\n exists: false\n message: *handoff_initial_prompt\n routing:\n kind: spawn\n - name: thread-reply\n events:\n - chat.message.mentioned\n - chat.message.subscribed\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n $.auto.attributions:\n exists: true\n message: |\n {{message.author.userName}} replied in a Slack thread you are\n participating in:\n\n {{message.text}}\n\n Channel: {{chat.channelId}}\n Thread: {{chat.threadId}}\n\n Treat this as steering for your in-flight work. Acknowledge in the\n thread when it changes what you are doing.\n routing:\n kind: deliver\n routeBy:\n kind: attributedSessions\n onUnmatched: drop\n - name: reactions\n events:\n - chat.reaction.added\n - chat.reaction.removed\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.message.author.isMe: true\n $.reaction.user.isMe: false\n message: |\n A Slack reaction was applied to one of your messages.\n\n Reaction: {{reaction.rawEmoji}} from {{reaction.user.userName}}\n Reacted-to message id: {{chat.messageId}}\n\n Inspect the thread if needed. Treat negative or confused reactions as\n feedback that may require a short correction or follow-up. Positive\n acknowledgements usually do not need a text reply.\n routing:\n kind: deliver\n routeBy:\n kind: attributedSessions\n onUnmatched: drop\n'
|
|
30975
|
+
},
|
|
30976
|
+
{
|
|
30977
|
+
path: "agents/handoff.yaml",
|
|
30978
|
+
content: 'name: handoff\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nidentity:\n displayName: Handoff\n username: handoff\n avatar:\n asset: .auto/assets/handoff.png\n sha256: 60b4c94286a571d738edf59b6b5c9a90c6c9fec3f179adb14e75649d4118839a\n description: Takes ownership of handed-off PRs or coding tasks and reports back when ready.\nimports:\n - ../fragments/environments/agent-runtime.yaml\nsystemPrompt: |\n You are the handoff coder for {{ $repoFullName }}.\n\n A user or another Auto agent has handed work to you through GitHub.\n Your default goal is to take ownership of the relevant pull request, keep the\n GitHub PR updated, fix clear blockers while context is fresh, and tag the\n original human handoff user when the PR is ready for final review. If no PR\n exists yet, create one for the requested implementation.\n\n Work from the mounted {{ $repoFullName }} checkout. Read README.md, AGENTS.md,\n CONTRIBUTING.md, CLAUDE.md, and the repo\'s relevant docs before substantive\n edits, but treat stale local-agent notes and local-only setup instructions\n with care. Adapt to nearby code and established patterns. Do not revert\n unrelated changes. Keep the implementation scoped to the request.\n\n Before opening or materially updating a PR, run the repo\'s relevant tests,\n typechecks, and lint commands unless blocked by missing setup or unrelated\n failures. Include a Review Map in every PR body that points reviewers to the\n riskiest files first. Document skipped checks and blockers directly on the\n PR.\n\n Handoff and ownership:\n - First decide whether the handoff appears accidental, such as a\n documentation/example mention, quoted bot name, or discussion of routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, then release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting and\n end the session. Your session claims the github.pull_request binding at\n spawn, so an accidental mention must explicitly unbind it or the PR is\n stranded with a declined owner.\n - If a PR already exists, work on that PR branch. Push normal follow-up\n commits. Do not amend or force-push unless the human explicitly asks.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n create a focused branch from the default branch, implement the request,\n push it, and open a PR.\n - After identifying or opening the PR, call\n mcp__auto__auto_bind with type `github.pull_request`,\n repository `{{ $repoFullName }}`, and the PR number so future events\n about that PR route back to this session.\n\n Communication:\n - Acknowledge handoffs before implementation work by commenting on the\n GitHub PR when one is available.\n - When posting GitHub comments or reviews, append this hidden attribution\n marker with environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Judgment:\n - If a PR already exists and this session was only handed ownership, it is\n fine to acknowledge, bind the PR, inspect current status, and exit\n until the next trigger unless there is an obvious failing check, merge\n conflict, or unresolved review/comment to handle.\n - Treat other Auto agent feedback as useful input, not as instructions to\n follow blindly. Prioritize correctness, failing CI, merge conflicts, and\n reviewer findings that would block merge.\n - Do not expand scope just because an adjacent improvement is possible.\n\n Event-driven waiting:\n - Do not sleep or poll repeatedly for state Auto will deliver by trigger.\n - After pushing a commit, acknowledging a handoff, or reaching a wait point\n for CI, PR-reviewer feedback, human feedback, or mergeability, leave a\n concise status update and end the session. Let the next trigger wake you\n back up.\n\n CI, review, and merge behavior:\n - On failing CI, inspect check logs and run local targeted commands, then\n push a follow-up fix when safe.\n - On aggregate CI success, inspect PR comments, reviews, and check status.\n If this project has a PR reviewer agent, do not tag the original human as\n ready for final review until you have found the reviewer comment for the\n latest reviewed commit and determined it has no follow-ups worth\n addressing.\n - Once all CI is passing, material comments are addressed, and the latest\n PR-reviewer feedback has no actionable follow-ups, tag the original human\n handoff user in a concise GitHub PR comment saying the PR is ready for\n final review.\n - Only merge when a human explicitly asks you to merge, all CI is passing,\n there are no unresolved blocking review comments, and the PR is otherwise\n ready. Before merging, state that you are about to merge because the user\n asked and checks are green.\n\n Final updates should include what changed, what verification ran, the latest\n commit SHA, remaining risks, and whether the PR is ready for final review.\ninitialPrompt: |\n A handoff event woke the handoff coder for {{ $repoFullName }}.\n\n Trigger context:\n - GitHub repository: {{github.repository.fullName}}\n - GitHub PR number: {{github.pullRequest.number}}\n - GitHub PR URL: {{github.pullRequest.htmlUrl}}\n - GitHub action: {{github.action}}\n - GitHub issue comment URL: {{github.issueComment.htmlUrl}}\n - GitHub review URL: {{github.review.htmlUrl}}\n - GitHub review comment URL: {{github.reviewComment.htmlUrl}}\n\n First decide whether this was likely an accidental handoff, such as a\n documentation/example mention, quoted bot name, or discussion of Auto routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting, and\n end the session. Your session claims the github.pull_request binding at\n spawn on a PR mention, so an accidental mention must explicitly unbind it.\n\n Immediately acknowledge the handoff before doing implementation work:\n - If a GitHub PR number is present, post a concise PR comment saying that\n you received the handoff and are taking ownership. Append the hidden\n attribution marker required by your instructions.\n\n Then establish PR context:\n - If the trigger includes a GitHub PR, inspect it with pull_request_read.\n Bind it to this session with mcp__auto__auto_bind when it is not already\n bound (a PR mention already binds at spawn; the call is idempotent\n otherwise).\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n implement from the default branch, open a focused PR, and bind your\n session to the new PR.\nmounts:\n - kind: git\n repository: "{{ $repoFullName }}"\n mountPath: /workspace/repo\n ref: main\n auth:\n kind: githubApp\n capabilities:\n contents: write\n pullRequests: write\n issues: write\n checks: read\n actions: read\n workflows: write\nworkingDirectory: /workspace/repo\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - create_pull_request\n - update_pull_request\n - merge_pull_request\n - add_issue_comment\n - issue_read\n - search_pull_requests\n - actions_get\n - actions_list\ntriggers:\n - name: github-handoff\n events:\n - github.pull_request.opened\n - github.issue_comment.created\n - github.pull_request_review.submitted\n - github.pull_request_review_comment.created\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.mentioned: true\n $.github.auto.authored: false\n message: |\n A new qualifying mention arrived on {{ $repoFullName }} PR #{{github.pullRequest.number}}\n (action: {{github.action}}).\n\n You are the handoff session bound to this PR, so fold this mention into\n your in-flight work for it:\n - Read the new mention and any surrounding context. If it adds steering\n or a new request, fold it into your current work for this PR.\n - If the mention is from a human, acknowledge it promptly on GitHub.\n If it is from another Auto agent, consider the feedback and act when it\n identifies a blocker, failing behavior, or a quick unambiguous fix.\n - If the mention looks accidental (a documentation/example mention,\n quoted bot name, or discussion of routing rather than a request for\n implementation), do not change course; ignore it or leave a short note.\n - Keep work on the existing PR branch. Do not amend, force-push, or open\n a replacement PR.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: github-handoff-edited\n events:\n - github.pull_request.edited\n - github.issue_comment.edited\n - github.pull_request_review.edited\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.mentioned:\n changedTo: true\n $.github.auto.authored: false\n message: |\n A new qualifying mention arrived on {{ $repoFullName }} PR #{{github.pullRequest.number}}\n (action: {{github.action}}).\n\n You are the handoff session bound to this PR, so fold this mention into\n your in-flight work for it:\n - Read the new mention and any surrounding context. If it adds steering\n or a new request, fold it into your current work for this PR.\n - If the mention is from a human, acknowledge it promptly on GitHub.\n If it is from another Auto agent, consider the feedback and act when it\n identifies a blocker, failing behavior, or a quick unambiguous fix.\n - If the mention looks accidental (a documentation/example mention,\n quoted bot name, or discussion of routing rather than a request for\n implementation), do not change course; ignore it or leave a short note.\n - Keep work on the existing PR branch. Do not amend, force-push, or open\n a replacement PR.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: pr-conversation\n events:\n - github.issue_comment.created\n - github.issue_comment.edited\n - github.pull_request_review.submitted\n - github.pull_request_review.edited\n - github.pull_request_review_comment.created\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.authored: false\n message: |\n A GitHub PR conversation update arrived for {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n\n Read the update and decide whether it requires action. If it is from a\n human, acknowledge it promptly on GitHub. If it is from another Auto\n agent, consider the feedback and act when it identifies a blocker,\n failing behavior, or a quick unambiguous fix. Keep work on the existing\n PR branch.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: check-failed\n event: github.check_run.completed\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.checkRun.conclusion: failure\n $.github.checkRun.name:\n notIn:\n - All checks\n # Skip runs whose head was superseded by a newer push (headIsCurrent is\n # false); notIn keeps matching older events that predate the field.\n $.github.checkRun.headIsCurrent:\n notIn:\n - false\n message: |\n Check {{github.checkRun.name}} failed on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Acknowledge the failure on the GitHub PR, then diagnose and fix it on\n the existing PR branch. Do not amend, force-push, or open a replacement\n PR. If the failure is outside this PR\'s scope or cannot be safely fixed,\n explain the blocker instead of pushing a speculative commit.\n\n Check session URL: {{github.checkRun.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: ci-green\n event: github.check_run.completed\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.checkRun.conclusion: success\n $.github.checkRun.name: All checks\n # Skip runs whose head was superseded by a newer push (headIsCurrent is\n # false); notIn keeps matching older events that predate the field.\n $.github.checkRun.headIsCurrent:\n notIn:\n - false\n message: |\n Aggregate CI passed on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Inspect PR comments, reviews, and checks. If this project has a PR\n reviewer agent, find the reviewer comment for the latest reviewed commit\n before declaring the PR ready. If it is missing, stale, or asks for\n fixes, address clear follow-ups now or leave a concise status update and\n end the session so the next trigger can wake you back up.\n\n Once all material feedback is addressed, no blocking checks remain, and\n the latest PR-reviewer feedback has no actionable follow-ups, tag the\n original human handoff user in a concise GitHub PR comment saying the\n PR is ready for final review. Do not merge unless a human explicitly\n asked you to merge.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: merge-conflict\n event: github.pull_request.merge_conflict\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n message: |\n A merge conflict was detected on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Acknowledge the conflict on GitHub. Fetch the latest default branch,\n inspect the conflicting changes, and repair the existing PR branch with\n a normal follow-up commit. Do not amend, force-push, or open a\n replacement PR.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n'
|
|
30979
|
+
},
|
|
30980
|
+
{
|
|
30981
|
+
path: "fragments/environments/agent-runtime.yaml",
|
|
30982
|
+
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
30983
|
+
}
|
|
30984
|
+
]
|
|
30951
30985
|
}
|
|
30952
30986
|
],
|
|
30953
30987
|
"@auto/incident-response": [
|
|
@@ -67372,6 +67406,98 @@ function tomlString(value2) {
|
|
|
67372
67406
|
return `"${value2.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
67373
67407
|
}
|
|
67374
67408
|
|
|
67409
|
+
// src/commands/agent-bridge/harness/codex/turn-retry.ts
|
|
67410
|
+
var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
|
|
67411
|
+
var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
|
|
67412
|
+
var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
|
|
67413
|
+
var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
|
|
67414
|
+
var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
|
|
67415
|
+
var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
|
|
67416
|
+
function classifyRetryableCodexTurnFailure(message) {
|
|
67417
|
+
if (!message) {
|
|
67418
|
+
return null;
|
|
67419
|
+
}
|
|
67420
|
+
const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
|
|
67421
|
+
const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
|
|
67422
|
+
if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
|
|
67423
|
+
return null;
|
|
67424
|
+
}
|
|
67425
|
+
return {
|
|
67426
|
+
cause: "rate_limit",
|
|
67427
|
+
message,
|
|
67428
|
+
retryAfterMs
|
|
67429
|
+
};
|
|
67430
|
+
}
|
|
67431
|
+
function codexTurnRetryDelayMs(input) {
|
|
67432
|
+
if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
|
|
67433
|
+
return null;
|
|
67434
|
+
}
|
|
67435
|
+
const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
|
|
67436
|
+
if (remainingDelayBudgetMs <= 0) {
|
|
67437
|
+
return null;
|
|
67438
|
+
}
|
|
67439
|
+
const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
|
|
67440
|
+
return delayMs <= remainingDelayBudgetMs ? delayMs : null;
|
|
67441
|
+
}
|
|
67442
|
+
function isRateLimitMessage(message) {
|
|
67443
|
+
return /\b(?:too many requests|tokens per min|tpm)\b/i.test(message) || /\b(?:http\s*)?(?:status(?:\s*code)?|code)\s*[:=]?\s*429\b/i.test(
|
|
67444
|
+
message
|
|
67445
|
+
) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
|
|
67446
|
+
}
|
|
67447
|
+
function parseExplicitRetryAfterMs(message) {
|
|
67448
|
+
return parseDelayMs(
|
|
67449
|
+
message.match(
|
|
67450
|
+
/\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
67451
|
+
)
|
|
67452
|
+
);
|
|
67453
|
+
}
|
|
67454
|
+
function parseTryAgainDelayMs(message) {
|
|
67455
|
+
return parseDelayMs(
|
|
67456
|
+
message.match(
|
|
67457
|
+
/\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
67458
|
+
)
|
|
67459
|
+
);
|
|
67460
|
+
}
|
|
67461
|
+
function parseDelayMs(match) {
|
|
67462
|
+
if (!match) {
|
|
67463
|
+
return null;
|
|
67464
|
+
}
|
|
67465
|
+
const value2 = Number.parseFloat(match[1] ?? "");
|
|
67466
|
+
if (!Number.isFinite(value2) || value2 < 0) {
|
|
67467
|
+
return null;
|
|
67468
|
+
}
|
|
67469
|
+
switch ((match[2] ?? "s").toLowerCase()) {
|
|
67470
|
+
case "millisecond":
|
|
67471
|
+
case "milliseconds":
|
|
67472
|
+
case "msec":
|
|
67473
|
+
case "msecs":
|
|
67474
|
+
case "ms":
|
|
67475
|
+
return Math.round(value2);
|
|
67476
|
+
case "minute":
|
|
67477
|
+
case "minutes":
|
|
67478
|
+
case "min":
|
|
67479
|
+
case "mins":
|
|
67480
|
+
case "m":
|
|
67481
|
+
return Math.round(value2 * 6e4);
|
|
67482
|
+
default:
|
|
67483
|
+
return Math.round(value2 * 1e3);
|
|
67484
|
+
}
|
|
67485
|
+
}
|
|
67486
|
+
function boundedRetryAfterMs(delayMs) {
|
|
67487
|
+
return Math.min(
|
|
67488
|
+
Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
|
|
67489
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
67490
|
+
);
|
|
67491
|
+
}
|
|
67492
|
+
function jitteredBackoffMs(attempt) {
|
|
67493
|
+
const baseDelayMs = Math.min(
|
|
67494
|
+
CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
|
|
67495
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
67496
|
+
);
|
|
67497
|
+
const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
|
|
67498
|
+
return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
|
|
67499
|
+
}
|
|
67500
|
+
|
|
67375
67501
|
// src/commands/agent-bridge/harness/codex/session.ts
|
|
67376
67502
|
var CODEX_REQUEST_TIMEOUT_MS = 3e4;
|
|
67377
67503
|
var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
|
|
@@ -67380,6 +67506,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
|
67380
67506
|
"fileChange",
|
|
67381
67507
|
"mcpToolCall"
|
|
67382
67508
|
]);
|
|
67509
|
+
var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
|
|
67383
67510
|
function startCodexAgentBridgeSession(input) {
|
|
67384
67511
|
return new CodexAgentBridgeSessionImpl(input);
|
|
67385
67512
|
}
|
|
@@ -67404,6 +67531,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67404
67531
|
// Messages held in "deferred" mode while a turn is in flight; flushed as a
|
|
67405
67532
|
// fresh turn once the active turn completes.
|
|
67406
67533
|
deferredMessages = [];
|
|
67534
|
+
turnRetry = null;
|
|
67535
|
+
pendingRetryableError = null;
|
|
67407
67536
|
constructor(input) {
|
|
67408
67537
|
this.input = input;
|
|
67409
67538
|
}
|
|
@@ -67414,6 +67543,13 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67414
67543
|
await this.ensureStarted();
|
|
67415
67544
|
const threadId = this.requireThreadId();
|
|
67416
67545
|
const mode = options?.mode ?? "interrupt";
|
|
67546
|
+
if (this.turnRetry !== null) {
|
|
67547
|
+
this.deferredMessages.push(message);
|
|
67548
|
+
this.input.writeOutput?.(
|
|
67549
|
+
"agent_bridge_codex_message_deferred reason=turn_retry"
|
|
67550
|
+
);
|
|
67551
|
+
return;
|
|
67552
|
+
}
|
|
67417
67553
|
if (this.activeTurnId === null) {
|
|
67418
67554
|
await this.startTurn(threadId, message);
|
|
67419
67555
|
return;
|
|
@@ -67449,6 +67585,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67449
67585
|
});
|
|
67450
67586
|
}
|
|
67451
67587
|
this.activeTurnId = null;
|
|
67588
|
+
this.turnRetry = null;
|
|
67589
|
+
this.clearPendingRetryableError();
|
|
67452
67590
|
this.pendingToolItemIds.clear();
|
|
67453
67591
|
this.resolveSettlement();
|
|
67454
67592
|
this.rejectAllPending(new Error("Codex session is closed"));
|
|
@@ -67551,6 +67689,12 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67551
67689
|
input: [userTextInput(message)]
|
|
67552
67690
|
});
|
|
67553
67691
|
}
|
|
67692
|
+
async startContinuationTurn(threadId) {
|
|
67693
|
+
await this.request("turn/start", {
|
|
67694
|
+
threadId,
|
|
67695
|
+
input: []
|
|
67696
|
+
});
|
|
67697
|
+
}
|
|
67554
67698
|
// Inject a message into the active turn. Prefers `turn/steer` (codex folds the
|
|
67555
67699
|
// input into the running turn and owns transcript consistency); falls back to a
|
|
67556
67700
|
// hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
|
|
@@ -67671,10 +67815,7 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67671
67815
|
this.settleResponse(message.id, message.result, message.error);
|
|
67672
67816
|
return;
|
|
67673
67817
|
case "notification":
|
|
67674
|
-
this.
|
|
67675
|
-
this.enqueueCallback(
|
|
67676
|
-
() => this.input.onNotification(message.notification)
|
|
67677
|
-
);
|
|
67818
|
+
this.handleNotification(message.notification);
|
|
67678
67819
|
return;
|
|
67679
67820
|
case "serverRequest":
|
|
67680
67821
|
this.enqueueCallback(() => this.input.onServerRequest(message.request));
|
|
@@ -67715,6 +67856,156 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67715
67856
|
}
|
|
67716
67857
|
});
|
|
67717
67858
|
}
|
|
67859
|
+
handleNotification(notification) {
|
|
67860
|
+
if (notification.type === "turnCompleted") {
|
|
67861
|
+
this.handleTurnCompletedNotification(notification);
|
|
67862
|
+
return;
|
|
67863
|
+
}
|
|
67864
|
+
if (notification.type === "error") {
|
|
67865
|
+
this.handleErrorNotification(notification);
|
|
67866
|
+
return;
|
|
67867
|
+
}
|
|
67868
|
+
this.trackNotification(notification);
|
|
67869
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67870
|
+
}
|
|
67871
|
+
handleErrorNotification(notification) {
|
|
67872
|
+
const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
|
|
67873
|
+
if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
|
|
67874
|
+
this.holdRetryableError(notification, {
|
|
67875
|
+
releaseOnTimeout: this.turnRetry === null
|
|
67876
|
+
});
|
|
67877
|
+
return;
|
|
67878
|
+
}
|
|
67879
|
+
this.trackNotification(notification);
|
|
67880
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67881
|
+
}
|
|
67882
|
+
handleTurnCompletedNotification(notification) {
|
|
67883
|
+
if (notification.status !== "failed") {
|
|
67884
|
+
this.turnRetry = null;
|
|
67885
|
+
this.clearPendingRetryableError();
|
|
67886
|
+
this.trackNotification(notification);
|
|
67887
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67888
|
+
return;
|
|
67889
|
+
}
|
|
67890
|
+
const failure = classifyRetryableCodexTurnFailure(
|
|
67891
|
+
notification.errorMessage
|
|
67892
|
+
);
|
|
67893
|
+
if (!failure) {
|
|
67894
|
+
this.turnRetry = null;
|
|
67895
|
+
this.clearPendingRetryableError();
|
|
67896
|
+
this.trackNotification(notification);
|
|
67897
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67898
|
+
return;
|
|
67899
|
+
}
|
|
67900
|
+
this.clearPendingRetryableError();
|
|
67901
|
+
const currentRetry = this.turnRetry ?? {
|
|
67902
|
+
originalFailure: notification,
|
|
67903
|
+
attempts: 0,
|
|
67904
|
+
cumulativeDelayMs: 0
|
|
67905
|
+
};
|
|
67906
|
+
const nextAttempt = currentRetry.attempts + 1;
|
|
67907
|
+
const delayMs = codexTurnRetryDelayMs({
|
|
67908
|
+
attempt: nextAttempt,
|
|
67909
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs,
|
|
67910
|
+
failure
|
|
67911
|
+
});
|
|
67912
|
+
if (delayMs === null) {
|
|
67913
|
+
this.turnRetry = null;
|
|
67914
|
+
this.settleCompletedTurn({
|
|
67915
|
+
flushDeferred: true,
|
|
67916
|
+
turnId: notification.turnId
|
|
67917
|
+
});
|
|
67918
|
+
this.input.writeOutput?.(
|
|
67919
|
+
`agent_bridge_codex_turn_retry_exhausted attempts=${currentRetry.attempts} total_delay_ms=${currentRetry.cumulativeDelayMs} cause=${failure.cause} original_error=${singleLine(currentRetry.originalFailure.errorMessage ?? "unknown error")}`
|
|
67920
|
+
);
|
|
67921
|
+
this.enqueueCallback(
|
|
67922
|
+
() => this.input.onNotification(currentRetry.originalFailure)
|
|
67923
|
+
);
|
|
67924
|
+
return;
|
|
67925
|
+
}
|
|
67926
|
+
this.turnRetry = {
|
|
67927
|
+
originalFailure: currentRetry.originalFailure,
|
|
67928
|
+
attempts: nextAttempt,
|
|
67929
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
|
|
67930
|
+
};
|
|
67931
|
+
this.settleCompletedTurn({
|
|
67932
|
+
flushDeferred: false,
|
|
67933
|
+
turnId: notification.turnId
|
|
67934
|
+
});
|
|
67935
|
+
this.scheduleFailedTurnRetry({
|
|
67936
|
+
attempt: nextAttempt,
|
|
67937
|
+
delayMs,
|
|
67938
|
+
failure,
|
|
67939
|
+
originalFailure: currentRetry.originalFailure
|
|
67940
|
+
});
|
|
67941
|
+
}
|
|
67942
|
+
holdRetryableError(notification, input) {
|
|
67943
|
+
this.clearPendingRetryableError();
|
|
67944
|
+
this.input.writeOutput?.(
|
|
67945
|
+
`agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
|
|
67946
|
+
);
|
|
67947
|
+
const timer = setTimeout(() => {
|
|
67948
|
+
if (this.pendingRetryableError?.notification !== notification) {
|
|
67949
|
+
return;
|
|
67950
|
+
}
|
|
67951
|
+
this.pendingRetryableError = null;
|
|
67952
|
+
if (!input.releaseOnTimeout) {
|
|
67953
|
+
return;
|
|
67954
|
+
}
|
|
67955
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67956
|
+
}, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
|
|
67957
|
+
timer.unref?.();
|
|
67958
|
+
this.pendingRetryableError = { notification, timer };
|
|
67959
|
+
}
|
|
67960
|
+
clearPendingRetryableError() {
|
|
67961
|
+
if (!this.pendingRetryableError) {
|
|
67962
|
+
return;
|
|
67963
|
+
}
|
|
67964
|
+
clearTimeout(this.pendingRetryableError.timer);
|
|
67965
|
+
this.pendingRetryableError = null;
|
|
67966
|
+
}
|
|
67967
|
+
scheduleFailedTurnRetry(input) {
|
|
67968
|
+
this.input.writeOutput?.(
|
|
67969
|
+
`agent_bridge_codex_turn_retry_scheduled attempt=${input.attempt} max_attempts=${MAX_CODEX_TURN_RETRY_ATTEMPTS} delay_ms=${input.delayMs} cause=${input.failure.cause} error=${singleLine(input.failure.message)}`
|
|
67970
|
+
);
|
|
67971
|
+
const timer = setTimeout(() => {
|
|
67972
|
+
void this.retryFailedTurn(input).catch((error51) => {
|
|
67973
|
+
this.enqueueCallback(() => this.input.onError(error51));
|
|
67974
|
+
});
|
|
67975
|
+
}, input.delayMs);
|
|
67976
|
+
timer.unref?.();
|
|
67977
|
+
}
|
|
67978
|
+
async retryFailedTurn(input) {
|
|
67979
|
+
if (this.closed) {
|
|
67980
|
+
return;
|
|
67981
|
+
}
|
|
67982
|
+
const threadId = this.threadId;
|
|
67983
|
+
if (threadId === null) {
|
|
67984
|
+
this.turnRetry = null;
|
|
67985
|
+
this.clearPendingRetryableError();
|
|
67986
|
+
this.flushDeferredMessages();
|
|
67987
|
+
this.enqueueCallback(
|
|
67988
|
+
() => this.input.onNotification(input.originalFailure)
|
|
67989
|
+
);
|
|
67990
|
+
return;
|
|
67991
|
+
}
|
|
67992
|
+
try {
|
|
67993
|
+
this.input.writeOutput?.(
|
|
67994
|
+
`agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
|
|
67995
|
+
);
|
|
67996
|
+
await this.startContinuationTurn(threadId);
|
|
67997
|
+
} catch (error51) {
|
|
67998
|
+
this.input.writeOutput?.(
|
|
67999
|
+
`agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
|
|
68000
|
+
);
|
|
68001
|
+
this.turnRetry = null;
|
|
68002
|
+
this.clearPendingRetryableError();
|
|
68003
|
+
this.flushDeferredMessages();
|
|
68004
|
+
this.enqueueCallback(
|
|
68005
|
+
() => this.input.onNotification(input.originalFailure)
|
|
68006
|
+
);
|
|
68007
|
+
}
|
|
68008
|
+
}
|
|
67718
68009
|
// Track turn/item lifecycle so steer/interrupt target the live turn and gate on
|
|
67719
68010
|
// tool-item settlement.
|
|
67720
68011
|
trackNotification(notification) {
|
|
@@ -67723,12 +68014,10 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67723
68014
|
this.activeTurnId = notification.turnId;
|
|
67724
68015
|
return;
|
|
67725
68016
|
case "turnCompleted":
|
|
67726
|
-
|
|
67727
|
-
|
|
67728
|
-
|
|
67729
|
-
|
|
67730
|
-
this.resolveSettlement();
|
|
67731
|
-
this.flushDeferredMessages();
|
|
68017
|
+
this.settleCompletedTurn({
|
|
68018
|
+
flushDeferred: true,
|
|
68019
|
+
turnId: notification.turnId
|
|
68020
|
+
});
|
|
67732
68021
|
return;
|
|
67733
68022
|
case "itemStarted":
|
|
67734
68023
|
if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
|
|
@@ -67745,6 +68034,16 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67745
68034
|
return;
|
|
67746
68035
|
}
|
|
67747
68036
|
}
|
|
68037
|
+
settleCompletedTurn(input) {
|
|
68038
|
+
if (input.turnId === this.activeTurnId) {
|
|
68039
|
+
this.activeTurnId = null;
|
|
68040
|
+
}
|
|
68041
|
+
this.pendingToolItemIds.clear();
|
|
68042
|
+
this.resolveSettlement();
|
|
68043
|
+
if (input.flushDeferred) {
|
|
68044
|
+
this.flushDeferredMessages();
|
|
68045
|
+
}
|
|
68046
|
+
}
|
|
67748
68047
|
onProcessExit(code) {
|
|
67749
68048
|
this.rejectAllPending(
|
|
67750
68049
|
new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
|
|
@@ -67841,6 +68140,9 @@ function threadIdFromResult(result) {
|
|
|
67841
68140
|
function errorMessage2(error51) {
|
|
67842
68141
|
return error51 instanceof Error ? error51.message : String(error51);
|
|
67843
68142
|
}
|
|
68143
|
+
function singleLine(message) {
|
|
68144
|
+
return message.replace(/\s+/g, " ").trim();
|
|
68145
|
+
}
|
|
67844
68146
|
|
|
67845
68147
|
// src/commands/agent-bridge/harness/codex/index.ts
|
|
67846
68148
|
function createCodexCommandHandler(input) {
|
package/dist/index.js
CHANGED
|
@@ -22691,6 +22691,23 @@ triggers:
|
|
|
22691
22691
|
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
22692
22692
|
}
|
|
22693
22693
|
]
|
|
22694
|
+
},
|
|
22695
|
+
{
|
|
22696
|
+
version: "1.5.0",
|
|
22697
|
+
files: [
|
|
22698
|
+
{
|
|
22699
|
+
path: "agents/pr-review-slack.yaml",
|
|
22700
|
+
content: 'imports:\n - ./pr-review.yaml\nsystemPrompt: |\n You are the code review agent for {{ $repoFullName }}.\n\n Read the repository\'s convention docs (README.md, CONTRIBUTING.md, AGENTS.md,\n CLAUDE.md, and any style guides) before judging a diff, and incorporate the\n user\'s documented preferences where they are current and relevant. Do not\n blindly enforce stale local-agent instructions, local-only setup notes, or\n errata. Confirm important preferences against the current repo shape and CI.\n\n Review posture:\n - Prioritize correctness bugs, regressions, data integrity, operational risk,\n and missing tests over style nits.\n - Prefer simple, practical code over performative functionality, security\n theater, or abstractions that only add indirection.\n - Prefer established local patterns over home-rolled machinery.\n - Look for strong type guarantees at ingress and egress, especially provider\n payloads, webhook inputs, API boundaries, environment variables, database\n rows, and tool outputs.\n - Look for real tests, especially at provider boundaries. Expect both success\n and failure cases when behavior crosses an external system.\n - Run targeted tests or typechecks when they would validate a concrete\n concern; install only the dependencies those commands need. Keep\n commands scoped to the PR.\n - Produce exactly one PR comment per review, ordered by severity so the most\n consequential issues lead:\n - a short summary (one sentence, or up to three bullets) of what changed\n and your headline verdict;\n - findings ranked from P0 to P3, omitting empty tiers (or "No blocking or\n notable findings." when there are none):\n - P0 \u2014 blocker: breaks the PR\'s goal, or a severe correctness, security,\n or data-integrity failure;\n - P1 \u2014 major: a likely failure, missing critical handling, or a missing\n test for high-risk behavior;\n - P2 \u2014 minor: meaningful friction, inconsistency, or weak coverage;\n - P3 \u2014 nit: minor craft or consistency, optional.\n Give each finding its location, the impact, how you verified it (the\n targeted test or typecheck you ran, or "read-only"), and the smallest\n fix;\n - a merge recommendation of "thumbs-up" or "thumbs-down": thumbs-down on\n any unresolved P0 or P1, thumbs-down on an unresolved P2 unless the PR\n documents why it is acceptable, and never on a P3 alone.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude the managed check with it), the managed check has\n been rolled onto the new head, and you re-begin the check and re-review\n against the pull request\'s current head. Keep exactly one current verdict\n per pull request at all times.\n\n When posting GitHub comments, append this hidden attribution marker with\n the environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Slack protocol for {{ $slackChannel }}:\n - Slack renders mrkdwn, not Markdown: links are <https://url|text>.\n - One top-level message per PR, shaped as\n "<pr-url|PR #N>: <pr title>". Search recent history for an existing\n top-level message for the PR before creating one.\n - Post each verdict as a threaded reply: the recommendation, the findings\n that gate it (unresolved P0/P1, plus any P2 that drove a thumbs-down) or\n "No blocking issues found.", a link to the PR comment, and the reviewed\n commit SHA.\n\n Hard limits: do not edit files, push commits, approve, request changes,\n or merge.\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in\n {{github.repository.fullName}}.\n\n Call checks.begin with { "name": "pr-review" } before doing anything else.\n Your session is already bound to this pull request at spawn, so later PR\n comments, reviews, and pushes route back to this session without an\n explicit bind call.\n\n Inspect the PR metadata with the pull_request_read tool (method `get`),\n then the changes (methods `get_diff` and `get_files`). Record the head\n commit SHA you reviewed.\n\n The local checkout is a shallow checkout of the PR head only. Fetch other\n refs explicitly if you need them.\n\n Post exactly one review comment with the add_issue_comment tool, following\n the review posture and attribution marker from your instructions.\n\n Then conclude the check: checks.success for a thumbs-up recommendation,\n checks.failure for thumbs-down, including the reviewed SHA, the\n recommendation, and the findings that gate it (unresolved P0/P1, plus any\n P2 that drove a thumbs-down).\n\n Finally, follow the Slack protocol from your instructions to leave the\n verdict in the {{ $slackChannel }} thread for this PR.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: "{{ $slackConnection }}"\ntriggers:\n - name: mention\n event: chat.message.mentioned\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n message: |\n {{message.author.userName}} mentioned you on Slack:\n\n {{message.text}}\n\n Channel: {{chat.channelId}}\n Thread: {{chat.threadId}}\n\n Reply in that thread with chat.send. If the user clearly links or names\n a PR, review it. If required context is missing, ask for the PR. Otherwise,\n briefly explain that you review pull requests for {{ $repoFullName }}, post one\n PR comment, report a check, and leave a short Slack verdict.\n routing:\n kind: spawn\n'
|
|
22701
|
+
},
|
|
22702
|
+
{
|
|
22703
|
+
path: "agents/pr-review.yaml",
|
|
22704
|
+
content: 'name: pr-review\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description: Reviews each pull request and posts one comment with a merge recommendation.\nimports:\n - ../fragments/environments/agent-runtime.yaml\nsystemPrompt: |\n You are the code review agent for {{ $repoFullName }}.\n\n Read the repository\'s convention docs (README.md, CONTRIBUTING.md, AGENTS.md,\n CLAUDE.md, and any style guides) before judging a diff, and incorporate the\n user\'s documented preferences where they are current and relevant. Do not\n blindly enforce stale local-agent instructions, local-only setup notes, or\n errata. Confirm important preferences against the current repo shape and CI.\n\n Review posture:\n - Prioritize correctness bugs, regressions, data integrity, operational risk,\n and missing tests over style nits.\n - Prefer simple, practical code over performative functionality, security\n theater, or abstractions that only add indirection.\n - Prefer established local patterns over home-rolled machinery.\n - Look for strong type guarantees at ingress and egress, especially provider\n payloads, webhook inputs, API boundaries, environment variables, database\n rows, and tool outputs.\n - Look for real tests, especially at provider boundaries. Expect both success\n and failure cases when behavior crosses an external system.\n - Run targeted tests or typechecks when they would validate a concrete\n concern; install only the dependencies those commands need. Keep\n commands scoped to the PR.\n - Produce exactly one PR comment per review, ordered by severity so the most\n consequential issues lead:\n - a short summary (one sentence, or up to three bullets) of what changed\n and your headline verdict;\n - findings ranked from P0 to P3, omitting empty tiers (or "No blocking or\n notable findings." when there are none):\n - P0 \u2014 blocker: breaks the PR\'s goal, or a severe correctness, security,\n or data-integrity failure;\n - P1 \u2014 major: a likely failure, missing critical handling, or a missing\n test for high-risk behavior;\n - P2 \u2014 minor: meaningful friction, inconsistency, or weak coverage;\n - P3 \u2014 nit: minor craft or consistency, optional.\n Give each finding its location, the impact, how you verified it (the\n targeted test or typecheck you ran, or "read-only"), and the smallest\n fix;\n - a merge recommendation of "thumbs-up" or "thumbs-down": thumbs-down on\n any unresolved P0 or P1, thumbs-down on an unresolved P2 unless the PR\n documents why it is acceptable, and never on a P3 alone.\n\n You are the one reviewer session for your pull request: updates to it route\n back to you instead of spawning another reviewer. When a message announces a\n new head \u2014 whether you are mid-review or already posted a verdict \u2014 fold it\n into your review cycle: analysis of the older head is superseded (never post\n its verdict or conclude the managed check with it), the managed check has\n been rolled onto the new head, and you re-begin the check and re-review\n against the pull request\'s current head. Keep exactly one current verdict\n per pull request at all times.\n\n When posting GitHub comments, append this hidden attribution marker with\n the environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Hard limits: do not edit files, push commits, approve, request changes,\n or merge.\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in\n {{github.repository.fullName}}.\n\n Call checks.begin with { "name": "pr-review" } before doing anything else.\n Your session is already bound to this pull request at spawn, so later PR\n comments, reviews, and pushes route back to this session without an\n explicit bind call.\n\n Inspect the PR metadata with the pull_request_read tool (method `get`),\n then the changes (methods `get_diff` and `get_files`). Record the head\n commit SHA you reviewed.\n\n The local checkout is a shallow checkout of the PR head only. Fetch other\n refs explicitly if you need them.\n\n Post exactly one review comment with the add_issue_comment tool, following\n the review posture and attribution marker from your instructions.\n\n Then conclude the check: checks.success for a thumbs-up recommendation,\n checks.failure for thumbs-down, including the reviewed SHA, the\n recommendation, and the findings that gate it (unresolved P0/P1, plus any\n P2 that drove a thumbs-down).\nmounts:\n - kind: git\n repository: "{{ $repoFullName }}"\n mountPath: /workspace/repo\n ref: refs/pull/{{payload.github.pullRequest.number}}/head\n depth: 1\n auth:\n kind: githubApp\n capabilities:\n contents: read\n pullRequests: write\n issues: write\n checks: read\n actions: read\nworkingDirectory: /workspace/repo\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - add_issue_comment\ntriggers:\n - name: pr-events\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n message: |\n Pull request #{{github.pullRequest.number}} in {{github.repository.fullName}} has a review-triggering\n update (action: {{github.action}}; current head {{github.pullRequest.headSha}}).\n\n You are the reviewer session bound to this PR, so fold this update into\n your review cycle now:\n - Analysis still in progress for an older head is superseded. Do not\n post its verdict and do not conclude the managed check with it. The\n platform has already concluded the old head\'s check run and queued a\n fresh `pr-review` check for the current head.\n - Call checks.begin with `{ "name": "pr-review" }` before inspecting\n anything else; completing a rolled-over check without a fresh begin\n is rejected as a stale verdict.\n - The local checkout still holds the head this session started from.\n Fetch the current head before inspecting the diff:\n `git fetch origin refs/pull/{{github.pullRequest.number}}/head` and\n check out the fetched commit.\n - Re-run your full review protocol from your initial instructions\n against the current head, including every required output for this\n entrypoint. Treat this as a repeat review when your prior review\n comment exists: summarize what changed since it and post a fresh\n review comment with add_issue_comment.\n - Conclude the check with checks.success or checks.failure for the\n current head\'s verdict. There must be exactly one current verdict\n for this PR.\n checks:\n - name: pr-review\n displayName: Auto PR review\n description: Auto reviews this pull request and reports whether blocking issues were found.\n instructions: |\n Call checks.begin with { "name": "pr-review" } before doing\n anything else. After posting the review comment, call\n checks.success for a thumbs-up recommendation or checks.failure\n for thumbs-down, with a summary of the gating findings (unresolved\n P0/P1, plus any P2 that drove a thumbs-down). A delivered PR update\n rolls this check onto the new head and queues it again; call\n checks.begin again before concluding that new cycle.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: pr-conversation\n events:\n - github.issue_comment.created\n - github.issue_comment.edited\n - github.pull_request_review.submitted\n - github.pull_request_review.edited\n - github.pull_request_review_comment.created\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.auto.authored: false\n message: |\n A PR conversation update arrived for {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n\n Read the update, incorporate any material reviewer or author context,\n and decide whether the pull request needs a refreshed review or a\n concrete blocker summary. Do not react to your own prior comments.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n'
|
|
22705
|
+
},
|
|
22706
|
+
{
|
|
22707
|
+
path: "fragments/environments/agent-runtime.yaml",
|
|
22708
|
+
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
22709
|
+
}
|
|
22710
|
+
]
|
|
22694
22711
|
}
|
|
22695
22712
|
],
|
|
22696
22713
|
"@auto/daily-digest": [
|
|
@@ -22802,6 +22819,23 @@ triggers:
|
|
|
22802
22819
|
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
22803
22820
|
}
|
|
22804
22821
|
]
|
|
22822
|
+
},
|
|
22823
|
+
{
|
|
22824
|
+
version: "1.5.0",
|
|
22825
|
+
files: [
|
|
22826
|
+
{
|
|
22827
|
+
path: "agents/handoff-slack.yaml",
|
|
22828
|
+
content: 'imports:\n - ./handoff.yaml\nsystemPrompt: |\n You are the handoff coder for {{ $repoFullName }}.\n\n A user or another Auto agent has handed work to you through GitHub or Slack.\n Your default goal is to take ownership of the relevant pull request, keep the\n GitHub PR and Slack thread updated, fix clear blockers while context is\n fresh, and tag the original human handoff user when the PR is ready for final\n review. If no PR exists yet, create one for the requested implementation.\n\n Work from the mounted {{ $repoFullName }} checkout. Read README.md, AGENTS.md,\n CONTRIBUTING.md, CLAUDE.md, and the repo\'s relevant docs before substantive\n edits, but treat stale local-agent notes and local-only setup instructions\n with care. Adapt to nearby code and established patterns. Do not revert\n unrelated changes. Keep the implementation scoped to the request.\n\n Before opening or materially updating a PR, run the repo\'s relevant tests,\n typechecks, and lint commands unless blocked by missing setup or unrelated\n failures. Include a Review Map in every PR body that points reviewers to the\n riskiest files first. Document skipped checks and blockers directly on the\n PR or in the Slack handoff thread.\n\n Handoff and ownership:\n - First decide whether the handoff appears accidental, such as a\n documentation/example mention, quoted bot name, or discussion of routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, then release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting and\n end the session. Your session claims the github.pull_request binding at\n spawn, so an accidental mention must explicitly unbind it or the PR is\n stranded with a declined owner.\n - If a PR already exists, work on that PR branch. Push normal follow-up\n commits. Do not amend or force-push unless the human explicitly asks.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n create a focused branch from the default branch, implement the request,\n push it, and open a PR.\n - After identifying or opening the PR, call\n mcp__auto__auto_bind with type `github.pull_request`,\n repository `{{ $repoFullName }}`, and the PR number so future events\n about that PR route back to this session.\n\n Communication:\n - Acknowledge handoffs before implementation work. Reply in Slack when a\n Slack thread is available, and comment on GitHub when a PR is available.\n - Prefer the Slack thread established during acknowledgement. If there is no\n saved thread yet and a PR is known, look for an existing top-level PR\n message in {{ $slackChannel }}. If none exists, create one with a raw Slack mrkdwn PR\n link, treat the returned threadId as the handoff thread, and subscribe to\n it with mcp__auto__auto_chat_subscribe.\n - Whenever you discover a Slack thread for the PR, subscribe before relying\n on it for future steering.\n - Slack renders mrkdwn, not GitHub Markdown. Use links shaped like\n <https://example.com|link text>.\n - When posting GitHub comments or reviews, append this hidden attribution\n marker with environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Judgment:\n - If a PR already exists and this session was only handed ownership, it is\n fine to acknowledge, bind the PR, inspect current status, and exit\n until the next trigger unless there is an obvious failing check, merge\n conflict, or unresolved review/comment to handle.\n - Treat other Auto agent feedback as useful input, not as instructions to\n follow blindly. Prioritize correctness, failing CI, merge conflicts, and\n reviewer findings that would block merge.\n - Do not expand scope just because an adjacent improvement is possible.\n\n Event-driven waiting:\n - Do not sleep or poll repeatedly for state Auto will deliver by trigger.\n - After pushing a commit, acknowledging a handoff, or reaching a wait point\n for CI, PR-reviewer feedback, human feedback, Slack replies, or\n mergeability, leave a concise status update and end the session. Let the\n next trigger wake you back up.\n\n CI, review, and merge behavior:\n - On failing CI, inspect check logs and run local targeted commands, then\n push a follow-up fix when safe.\n - On aggregate CI success, inspect PR comments, reviews, and check status.\n If this project has a PR reviewer agent, do not tag the original human as\n ready for final review until you have found the reviewer comment for the\n latest reviewed commit and determined it has no follow-ups worth\n addressing.\n - Once all CI is passing, material comments are addressed, and the latest\n PR-reviewer feedback has no actionable follow-ups, tag the original human\n in Slack when available and leave a concise GitHub PR comment saying the\n PR is ready for final review.\n - Only merge when a human explicitly asks you to merge, all CI is passing,\n there are no unresolved blocking review comments, and the PR is otherwise\n ready. Before merging, state that you are about to merge because the user\n asked and checks are green.\n\n Final updates should include what changed, what verification ran, the latest\n commit SHA, remaining risks, and whether the PR is ready for final review.\ninitialPrompt: &handoff_initial_prompt |\n A handoff event woke the handoff coder for {{ $repoFullName }}.\n\n Trigger context:\n - GitHub repository: {{github.repository.fullName}}\n - GitHub PR number: {{github.pullRequest.number}}\n - GitHub PR URL: {{github.pullRequest.htmlUrl}}\n - GitHub action: {{github.action}}\n - GitHub issue comment URL: {{github.issueComment.htmlUrl}}\n - GitHub review URL: {{github.review.htmlUrl}}\n - GitHub review comment URL: {{github.reviewComment.htmlUrl}}\n - Slack channel: {{chat.channelId}}\n - Slack thread: {{chat.threadId}}\n - Slack message author: {{message.author.userName}}\n - Slack message text: {{message.text}}\n\n First decide whether this was likely an accidental handoff, such as a\n documentation/example mention, quoted bot name, or discussion of Auto routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting, and\n end the session. Your session claims the github.pull_request binding at\n spawn on a PR mention, so an accidental mention must explicitly unbind it.\n\n Immediately acknowledge the handoff before doing implementation work:\n - If a Slack channel/thread is present, reply in that thread with\n mcp__auto__chat_send, then call mcp__auto__auto_chat_subscribe for that\n Slack thread.\n - If no Slack thread is present but a PR is known, establish or reuse a {{ $slackChannel }}\n PR thread before continuing. Search recent {{ $slackChannel }} history for the PR number\n or URL. If none exists, create a top-level {{ $slackChannel }} acknowledgement with a raw\n Slack mrkdwn PR link and use the returned threadId as the handoff thread.\n Subscribe before relying on the thread for future updates.\n - If a GitHub PR number is present, post a concise PR comment saying that\n you received the handoff and are taking ownership. Append the hidden\n attribution marker required by your instructions.\n - If both Slack and GitHub are available, acknowledge both.\n\n Then establish PR context:\n - If the trigger includes a GitHub PR, inspect it with pull_request_read.\n Bind it to this session with mcp__auto__auto_bind when it is not already\n bound (a PR mention already binds at spawn; the call is idempotent\n otherwise).\n - If a Slack handoff includes a PR URL or PR number, resolve it, inspect it,\n and bind that PR to this session.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n implement from the default branch, open a focused PR, bind your session to\n the new PR, and reply with the PR link in the Slack thread when one exists.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: "{{ $slackConnection }}"\ntriggers:\n - name: mention\n event: chat.message.mentioned\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n $.auto.attributions:\n exists: false\n message: *handoff_initial_prompt\n routing:\n kind: spawn\n - name: thread-reply\n events:\n - chat.message.mentioned\n - chat.message.subscribed\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.auto.authored: false\n $.auto.attributions:\n exists: true\n message: |\n {{message.author.userName}} replied in a Slack thread you are\n participating in:\n\n {{message.text}}\n\n Channel: {{chat.channelId}}\n Thread: {{chat.threadId}}\n\n Treat this as steering for your in-flight work. Acknowledge in the\n thread when it changes what you are doing.\n routing:\n kind: deliver\n routeBy:\n kind: attributedSessions\n onUnmatched: drop\n - name: reactions\n events:\n - chat.reaction.added\n - chat.reaction.removed\n connection: "{{ $slackConnection }}"\n where:\n $.chat.provider: slack\n $.message.author.isMe: true\n $.reaction.user.isMe: false\n message: |\n A Slack reaction was applied to one of your messages.\n\n Reaction: {{reaction.rawEmoji}} from {{reaction.user.userName}}\n Reacted-to message id: {{chat.messageId}}\n\n Inspect the thread if needed. Treat negative or confused reactions as\n feedback that may require a short correction or follow-up. Positive\n acknowledgements usually do not need a text reply.\n routing:\n kind: deliver\n routeBy:\n kind: attributedSessions\n onUnmatched: drop\n'
|
|
22829
|
+
},
|
|
22830
|
+
{
|
|
22831
|
+
path: "agents/handoff.yaml",
|
|
22832
|
+
content: 'name: handoff\nmodel:\n provider: anthropic\n id: claude-opus-4-8\nidentity:\n displayName: Handoff\n username: handoff\n avatar:\n asset: .auto/assets/handoff.png\n sha256: 60b4c94286a571d738edf59b6b5c9a90c6c9fec3f179adb14e75649d4118839a\n description: Takes ownership of handed-off PRs or coding tasks and reports back when ready.\nimports:\n - ../fragments/environments/agent-runtime.yaml\nsystemPrompt: |\n You are the handoff coder for {{ $repoFullName }}.\n\n A user or another Auto agent has handed work to you through GitHub.\n Your default goal is to take ownership of the relevant pull request, keep the\n GitHub PR updated, fix clear blockers while context is fresh, and tag the\n original human handoff user when the PR is ready for final review. If no PR\n exists yet, create one for the requested implementation.\n\n Work from the mounted {{ $repoFullName }} checkout. Read README.md, AGENTS.md,\n CONTRIBUTING.md, CLAUDE.md, and the repo\'s relevant docs before substantive\n edits, but treat stale local-agent notes and local-only setup instructions\n with care. Adapt to nearby code and established patterns. Do not revert\n unrelated changes. Keep the implementation scoped to the request.\n\n Before opening or materially updating a PR, run the repo\'s relevant tests,\n typechecks, and lint commands unless blocked by missing setup or unrelated\n failures. Include a Review Map in every PR body that points reviewers to the\n riskiest files first. Document skipped checks and blockers directly on the\n PR.\n\n Handoff and ownership:\n - First decide whether the handoff appears accidental, such as a\n documentation/example mention, quoted bot name, or discussion of routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, then release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting and\n end the session. Your session claims the github.pull_request binding at\n spawn, so an accidental mention must explicitly unbind it or the PR is\n stranded with a declined owner.\n - If a PR already exists, work on that PR branch. Push normal follow-up\n commits. Do not amend or force-push unless the human explicitly asks.\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n create a focused branch from the default branch, implement the request,\n push it, and open a PR.\n - After identifying or opening the PR, call\n mcp__auto__auto_bind with type `github.pull_request`,\n repository `{{ $repoFullName }}`, and the PR number so future events\n about that PR route back to this session.\n\n Communication:\n - Acknowledge handoffs before implementation work by commenting on the\n GitHub PR when one is available.\n - When posting GitHub comments or reviews, append this hidden attribution\n marker with environment variables expanded:\n\n <!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->\n\n Judgment:\n - If a PR already exists and this session was only handed ownership, it is\n fine to acknowledge, bind the PR, inspect current status, and exit\n until the next trigger unless there is an obvious failing check, merge\n conflict, or unresolved review/comment to handle.\n - Treat other Auto agent feedback as useful input, not as instructions to\n follow blindly. Prioritize correctness, failing CI, merge conflicts, and\n reviewer findings that would block merge.\n - Do not expand scope just because an adjacent improvement is possible.\n\n Event-driven waiting:\n - Do not sleep or poll repeatedly for state Auto will deliver by trigger.\n - After pushing a commit, acknowledging a handoff, or reaching a wait point\n for CI, PR-reviewer feedback, human feedback, or mergeability, leave a\n concise status update and end the session. Let the next trigger wake you\n back up.\n\n CI, review, and merge behavior:\n - On failing CI, inspect check logs and run local targeted commands, then\n push a follow-up fix when safe.\n - On aggregate CI success, inspect PR comments, reviews, and check status.\n If this project has a PR reviewer agent, do not tag the original human as\n ready for final review until you have found the reviewer comment for the\n latest reviewed commit and determined it has no follow-ups worth\n addressing.\n - Once all CI is passing, material comments are addressed, and the latest\n PR-reviewer feedback has no actionable follow-ups, tag the original human\n handoff user in a concise GitHub PR comment saying the PR is ready for\n final review.\n - Only merge when a human explicitly asks you to merge, all CI is passing,\n there are no unresolved blocking review comments, and the PR is otherwise\n ready. Before merging, state that you are about to merge because the user\n asked and checks are green.\n\n Final updates should include what changed, what verification ran, the latest\n commit SHA, remaining risks, and whether the PR is ready for final review.\ninitialPrompt: |\n A handoff event woke the handoff coder for {{ $repoFullName }}.\n\n Trigger context:\n - GitHub repository: {{github.repository.fullName}}\n - GitHub PR number: {{github.pullRequest.number}}\n - GitHub PR URL: {{github.pullRequest.htmlUrl}}\n - GitHub action: {{github.action}}\n - GitHub issue comment URL: {{github.issueComment.htmlUrl}}\n - GitHub review URL: {{github.review.htmlUrl}}\n - GitHub review comment URL: {{github.reviewComment.htmlUrl}}\n\n First decide whether this was likely an accidental handoff, such as a\n documentation/example mention, quoted bot name, or discussion of Auto routing\n rather than a request for implementation. If it looks accidental, do not\n take ownership. Leave one short note explaining why, release the\n spawn-claimed PR binding with mcp__auto__auto_unbind before exiting, and\n end the session. Your session claims the github.pull_request binding at\n spawn on a PR mention, so an accidental mention must explicitly unbind it.\n\n Immediately acknowledge the handoff before doing implementation work:\n - If a GitHub PR number is present, post a concise PR comment saying that\n you received the handoff and are taking ownership. Append the hidden\n attribution marker required by your instructions.\n\n Then establish PR context:\n - If the trigger includes a GitHub PR, inspect it with pull_request_read.\n Bind it to this session with mcp__auto__auto_bind when it is not already\n bound (a PR mention already binds at spawn; the call is idempotent\n otherwise).\n - If no PR exists, clarify only if the request is ambiguous. Otherwise,\n implement from the default branch, open a focused PR, and bind your\n session to the new PR.\nmounts:\n - kind: git\n repository: "{{ $repoFullName }}"\n mountPath: /workspace/repo\n ref: main\n auth:\n kind: githubApp\n capabilities:\n contents: write\n pullRequests: write\n issues: write\n checks: read\n actions: read\n workflows: write\nworkingDirectory: /workspace/repo\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - create_pull_request\n - update_pull_request\n - merge_pull_request\n - add_issue_comment\n - issue_read\n - search_pull_requests\n - actions_get\n - actions_list\ntriggers:\n - name: github-handoff\n events:\n - github.pull_request.opened\n - github.issue_comment.created\n - github.pull_request_review.submitted\n - github.pull_request_review_comment.created\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.mentioned: true\n $.github.auto.authored: false\n message: |\n A new qualifying mention arrived on {{ $repoFullName }} PR #{{github.pullRequest.number}}\n (action: {{github.action}}).\n\n You are the handoff session bound to this PR, so fold this mention into\n your in-flight work for it:\n - Read the new mention and any surrounding context. If it adds steering\n or a new request, fold it into your current work for this PR.\n - If the mention is from a human, acknowledge it promptly on GitHub.\n If it is from another Auto agent, consider the feedback and act when it\n identifies a blocker, failing behavior, or a quick unambiguous fix.\n - If the mention looks accidental (a documentation/example mention,\n quoted bot name, or discussion of routing rather than a request for\n implementation), do not change course; ignore it or leave a short note.\n - Keep work on the existing PR branch. Do not amend, force-push, or open\n a replacement PR.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: github-handoff-edited\n events:\n - github.pull_request.edited\n - github.issue_comment.edited\n - github.pull_request_review.edited\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.mentioned:\n changedTo: true\n $.github.auto.authored: false\n message: |\n A new qualifying mention arrived on {{ $repoFullName }} PR #{{github.pullRequest.number}}\n (action: {{github.action}}).\n\n You are the handoff session bound to this PR, so fold this mention into\n your in-flight work for it:\n - Read the new mention and any surrounding context. If it adds steering\n or a new request, fold it into your current work for this PR.\n - If the mention is from a human, acknowledge it promptly on GitHub.\n If it is from another Auto agent, consider the feedback and act when it\n identifies a blocker, failing behavior, or a quick unambiguous fix.\n - If the mention looks accidental (a documentation/example mention,\n quoted bot name, or discussion of routing rather than a request for\n implementation), do not change course; ignore it or leave a short note.\n - Keep work on the existing PR branch. Do not amend, force-push, or open\n a replacement PR.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: spawn\n - name: pr-conversation\n events:\n - github.issue_comment.created\n - github.issue_comment.edited\n - github.pull_request_review.submitted\n - github.pull_request_review.edited\n - github.pull_request_review_comment.created\n - github.pull_request_review_comment.edited\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.auto.authored: false\n message: |\n A GitHub PR conversation update arrived for {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Source URLs, when present:\n - issue comment: {{github.issueComment.htmlUrl}}\n - review: {{github.review.htmlUrl}}\n - review comment: {{github.reviewComment.htmlUrl}}\n\n Read the update and decide whether it requires action. If it is from a\n human, acknowledge it promptly on GitHub. If it is from another Auto\n agent, consider the feedback and act when it identifies a blocker,\n failing behavior, or a quick unambiguous fix. Keep work on the existing\n PR branch.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: check-failed\n event: github.check_run.completed\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.checkRun.conclusion: failure\n $.github.checkRun.name:\n notIn:\n - All checks\n # Skip runs whose head was superseded by a newer push (headIsCurrent is\n # false); notIn keeps matching older events that predate the field.\n $.github.checkRun.headIsCurrent:\n notIn:\n - false\n message: |\n Check {{github.checkRun.name}} failed on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Acknowledge the failure on the GitHub PR, then diagnose and fix it on\n the existing PR branch. Do not amend, force-push, or open a replacement\n PR. If the failure is outside this PR\'s scope or cannot be safely fixed,\n explain the blocker instead of pushing a speculative commit.\n\n Check session URL: {{github.checkRun.htmlUrl}}\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: ci-green\n event: github.check_run.completed\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n $.github.checkRun.conclusion: success\n $.github.checkRun.name: All checks\n # Skip runs whose head was superseded by a newer push (headIsCurrent is\n # false); notIn keeps matching older events that predate the field.\n $.github.checkRun.headIsCurrent:\n notIn:\n - false\n message: |\n Aggregate CI passed on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Inspect PR comments, reviews, and checks. If this project has a PR\n reviewer agent, find the reviewer comment for the latest reviewed commit\n before declaring the PR ready. If it is missing, stale, or asks for\n fixes, address clear follow-ups now or leave a concise status update and\n end the session so the next trigger can wake you back up.\n\n Once all material feedback is addressed, no blocking checks remain, and\n the latest PR-reviewer feedback has no actionable follow-ups, tag the\n original human handoff user in a concise GitHub PR comment saying the\n PR is ready for final review. Do not merge unless a human explicitly\n asked you to merge.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n - name: merge-conflict\n event: github.pull_request.merge_conflict\n connection: "{{ $githubConnection }}"\n where:\n $.github.repository.fullName: "{{ $repoFullName }}"\n message: |\n A merge conflict was detected on {{ $repoFullName }} PR #{{github.pullRequest.number}}.\n\n Acknowledge the conflict on GitHub. Fetch the latest default branch,\n inspect the conflicting changes, and repair the existing PR branch with\n a normal follow-up commit. Do not amend, force-push, or open a\n replacement PR.\n routing:\n kind: bind\n target: github.pull_request\n onUnmatched: drop\n'
|
|
22833
|
+
},
|
|
22834
|
+
{
|
|
22835
|
+
path: "fragments/environments/agent-runtime.yaml",
|
|
22836
|
+
content: "harness: claude-code\nenvironment:\n name: agent-runtime\n image:\n kind: preset\n name: node24\n resources:\n memoryMB: 8192\n"
|
|
22837
|
+
}
|
|
22838
|
+
]
|
|
22805
22839
|
}
|
|
22806
22840
|
],
|
|
22807
22841
|
"@auto/incident-response": [
|
|
@@ -30455,7 +30489,7 @@ var init_package = __esm({
|
|
|
30455
30489
|
"package.json"() {
|
|
30456
30490
|
package_default = {
|
|
30457
30491
|
name: "@autohq/cli",
|
|
30458
|
-
version: "0.1.
|
|
30492
|
+
version: "0.1.339",
|
|
30459
30493
|
license: "SEE LICENSE IN README.md",
|
|
30460
30494
|
publishConfig: {
|
|
30461
30495
|
access: "public"
|
|
@@ -45020,6 +45054,98 @@ function tomlString(value) {
|
|
|
45020
45054
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
45021
45055
|
}
|
|
45022
45056
|
|
|
45057
|
+
// src/commands/agent-bridge/harness/codex/turn-retry.ts
|
|
45058
|
+
var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
|
|
45059
|
+
var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
|
|
45060
|
+
var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
|
|
45061
|
+
var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
|
|
45062
|
+
var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
|
|
45063
|
+
var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
|
|
45064
|
+
function classifyRetryableCodexTurnFailure(message) {
|
|
45065
|
+
if (!message) {
|
|
45066
|
+
return null;
|
|
45067
|
+
}
|
|
45068
|
+
const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
|
|
45069
|
+
const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
|
|
45070
|
+
if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
|
|
45071
|
+
return null;
|
|
45072
|
+
}
|
|
45073
|
+
return {
|
|
45074
|
+
cause: "rate_limit",
|
|
45075
|
+
message,
|
|
45076
|
+
retryAfterMs
|
|
45077
|
+
};
|
|
45078
|
+
}
|
|
45079
|
+
function codexTurnRetryDelayMs(input) {
|
|
45080
|
+
if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
|
|
45081
|
+
return null;
|
|
45082
|
+
}
|
|
45083
|
+
const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
|
|
45084
|
+
if (remainingDelayBudgetMs <= 0) {
|
|
45085
|
+
return null;
|
|
45086
|
+
}
|
|
45087
|
+
const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
|
|
45088
|
+
return delayMs <= remainingDelayBudgetMs ? delayMs : null;
|
|
45089
|
+
}
|
|
45090
|
+
function isRateLimitMessage(message) {
|
|
45091
|
+
return /\b(?:too many requests|tokens per min|tpm)\b/i.test(message) || /\b(?:http\s*)?(?:status(?:\s*code)?|code)\s*[:=]?\s*429\b/i.test(
|
|
45092
|
+
message
|
|
45093
|
+
) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
|
|
45094
|
+
}
|
|
45095
|
+
function parseExplicitRetryAfterMs(message) {
|
|
45096
|
+
return parseDelayMs(
|
|
45097
|
+
message.match(
|
|
45098
|
+
/\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
45099
|
+
)
|
|
45100
|
+
);
|
|
45101
|
+
}
|
|
45102
|
+
function parseTryAgainDelayMs(message) {
|
|
45103
|
+
return parseDelayMs(
|
|
45104
|
+
message.match(
|
|
45105
|
+
/\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
45106
|
+
)
|
|
45107
|
+
);
|
|
45108
|
+
}
|
|
45109
|
+
function parseDelayMs(match) {
|
|
45110
|
+
if (!match) {
|
|
45111
|
+
return null;
|
|
45112
|
+
}
|
|
45113
|
+
const value = Number.parseFloat(match[1] ?? "");
|
|
45114
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
45115
|
+
return null;
|
|
45116
|
+
}
|
|
45117
|
+
switch ((match[2] ?? "s").toLowerCase()) {
|
|
45118
|
+
case "millisecond":
|
|
45119
|
+
case "milliseconds":
|
|
45120
|
+
case "msec":
|
|
45121
|
+
case "msecs":
|
|
45122
|
+
case "ms":
|
|
45123
|
+
return Math.round(value);
|
|
45124
|
+
case "minute":
|
|
45125
|
+
case "minutes":
|
|
45126
|
+
case "min":
|
|
45127
|
+
case "mins":
|
|
45128
|
+
case "m":
|
|
45129
|
+
return Math.round(value * 6e4);
|
|
45130
|
+
default:
|
|
45131
|
+
return Math.round(value * 1e3);
|
|
45132
|
+
}
|
|
45133
|
+
}
|
|
45134
|
+
function boundedRetryAfterMs(delayMs) {
|
|
45135
|
+
return Math.min(
|
|
45136
|
+
Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
|
|
45137
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
45138
|
+
);
|
|
45139
|
+
}
|
|
45140
|
+
function jitteredBackoffMs(attempt) {
|
|
45141
|
+
const baseDelayMs = Math.min(
|
|
45142
|
+
CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
|
|
45143
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
45144
|
+
);
|
|
45145
|
+
const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
|
|
45146
|
+
return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
|
|
45147
|
+
}
|
|
45148
|
+
|
|
45023
45149
|
// src/commands/agent-bridge/harness/codex/session.ts
|
|
45024
45150
|
var CODEX_REQUEST_TIMEOUT_MS = 3e4;
|
|
45025
45151
|
var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
|
|
@@ -45028,6 +45154,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
|
45028
45154
|
"fileChange",
|
|
45029
45155
|
"mcpToolCall"
|
|
45030
45156
|
]);
|
|
45157
|
+
var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
|
|
45031
45158
|
function startCodexAgentBridgeSession(input) {
|
|
45032
45159
|
return new CodexAgentBridgeSessionImpl(input);
|
|
45033
45160
|
}
|
|
@@ -45052,6 +45179,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45052
45179
|
// Messages held in "deferred" mode while a turn is in flight; flushed as a
|
|
45053
45180
|
// fresh turn once the active turn completes.
|
|
45054
45181
|
deferredMessages = [];
|
|
45182
|
+
turnRetry = null;
|
|
45183
|
+
pendingRetryableError = null;
|
|
45055
45184
|
constructor(input) {
|
|
45056
45185
|
this.input = input;
|
|
45057
45186
|
}
|
|
@@ -45062,6 +45191,13 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45062
45191
|
await this.ensureStarted();
|
|
45063
45192
|
const threadId = this.requireThreadId();
|
|
45064
45193
|
const mode = options?.mode ?? "interrupt";
|
|
45194
|
+
if (this.turnRetry !== null) {
|
|
45195
|
+
this.deferredMessages.push(message);
|
|
45196
|
+
this.input.writeOutput?.(
|
|
45197
|
+
"agent_bridge_codex_message_deferred reason=turn_retry"
|
|
45198
|
+
);
|
|
45199
|
+
return;
|
|
45200
|
+
}
|
|
45065
45201
|
if (this.activeTurnId === null) {
|
|
45066
45202
|
await this.startTurn(threadId, message);
|
|
45067
45203
|
return;
|
|
@@ -45097,6 +45233,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45097
45233
|
});
|
|
45098
45234
|
}
|
|
45099
45235
|
this.activeTurnId = null;
|
|
45236
|
+
this.turnRetry = null;
|
|
45237
|
+
this.clearPendingRetryableError();
|
|
45100
45238
|
this.pendingToolItemIds.clear();
|
|
45101
45239
|
this.resolveSettlement();
|
|
45102
45240
|
this.rejectAllPending(new Error("Codex session is closed"));
|
|
@@ -45199,6 +45337,12 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45199
45337
|
input: [userTextInput(message)]
|
|
45200
45338
|
});
|
|
45201
45339
|
}
|
|
45340
|
+
async startContinuationTurn(threadId) {
|
|
45341
|
+
await this.request("turn/start", {
|
|
45342
|
+
threadId,
|
|
45343
|
+
input: []
|
|
45344
|
+
});
|
|
45345
|
+
}
|
|
45202
45346
|
// Inject a message into the active turn. Prefers `turn/steer` (codex folds the
|
|
45203
45347
|
// input into the running turn and owns transcript consistency); falls back to a
|
|
45204
45348
|
// hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
|
|
@@ -45319,10 +45463,7 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45319
45463
|
this.settleResponse(message.id, message.result, message.error);
|
|
45320
45464
|
return;
|
|
45321
45465
|
case "notification":
|
|
45322
|
-
this.
|
|
45323
|
-
this.enqueueCallback(
|
|
45324
|
-
() => this.input.onNotification(message.notification)
|
|
45325
|
-
);
|
|
45466
|
+
this.handleNotification(message.notification);
|
|
45326
45467
|
return;
|
|
45327
45468
|
case "serverRequest":
|
|
45328
45469
|
this.enqueueCallback(() => this.input.onServerRequest(message.request));
|
|
@@ -45363,6 +45504,156 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45363
45504
|
}
|
|
45364
45505
|
});
|
|
45365
45506
|
}
|
|
45507
|
+
handleNotification(notification) {
|
|
45508
|
+
if (notification.type === "turnCompleted") {
|
|
45509
|
+
this.handleTurnCompletedNotification(notification);
|
|
45510
|
+
return;
|
|
45511
|
+
}
|
|
45512
|
+
if (notification.type === "error") {
|
|
45513
|
+
this.handleErrorNotification(notification);
|
|
45514
|
+
return;
|
|
45515
|
+
}
|
|
45516
|
+
this.trackNotification(notification);
|
|
45517
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45518
|
+
}
|
|
45519
|
+
handleErrorNotification(notification) {
|
|
45520
|
+
const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
|
|
45521
|
+
if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
|
|
45522
|
+
this.holdRetryableError(notification, {
|
|
45523
|
+
releaseOnTimeout: this.turnRetry === null
|
|
45524
|
+
});
|
|
45525
|
+
return;
|
|
45526
|
+
}
|
|
45527
|
+
this.trackNotification(notification);
|
|
45528
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45529
|
+
}
|
|
45530
|
+
handleTurnCompletedNotification(notification) {
|
|
45531
|
+
if (notification.status !== "failed") {
|
|
45532
|
+
this.turnRetry = null;
|
|
45533
|
+
this.clearPendingRetryableError();
|
|
45534
|
+
this.trackNotification(notification);
|
|
45535
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45536
|
+
return;
|
|
45537
|
+
}
|
|
45538
|
+
const failure = classifyRetryableCodexTurnFailure(
|
|
45539
|
+
notification.errorMessage
|
|
45540
|
+
);
|
|
45541
|
+
if (!failure) {
|
|
45542
|
+
this.turnRetry = null;
|
|
45543
|
+
this.clearPendingRetryableError();
|
|
45544
|
+
this.trackNotification(notification);
|
|
45545
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45546
|
+
return;
|
|
45547
|
+
}
|
|
45548
|
+
this.clearPendingRetryableError();
|
|
45549
|
+
const currentRetry = this.turnRetry ?? {
|
|
45550
|
+
originalFailure: notification,
|
|
45551
|
+
attempts: 0,
|
|
45552
|
+
cumulativeDelayMs: 0
|
|
45553
|
+
};
|
|
45554
|
+
const nextAttempt = currentRetry.attempts + 1;
|
|
45555
|
+
const delayMs = codexTurnRetryDelayMs({
|
|
45556
|
+
attempt: nextAttempt,
|
|
45557
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs,
|
|
45558
|
+
failure
|
|
45559
|
+
});
|
|
45560
|
+
if (delayMs === null) {
|
|
45561
|
+
this.turnRetry = null;
|
|
45562
|
+
this.settleCompletedTurn({
|
|
45563
|
+
flushDeferred: true,
|
|
45564
|
+
turnId: notification.turnId
|
|
45565
|
+
});
|
|
45566
|
+
this.input.writeOutput?.(
|
|
45567
|
+
`agent_bridge_codex_turn_retry_exhausted attempts=${currentRetry.attempts} total_delay_ms=${currentRetry.cumulativeDelayMs} cause=${failure.cause} original_error=${singleLine(currentRetry.originalFailure.errorMessage ?? "unknown error")}`
|
|
45568
|
+
);
|
|
45569
|
+
this.enqueueCallback(
|
|
45570
|
+
() => this.input.onNotification(currentRetry.originalFailure)
|
|
45571
|
+
);
|
|
45572
|
+
return;
|
|
45573
|
+
}
|
|
45574
|
+
this.turnRetry = {
|
|
45575
|
+
originalFailure: currentRetry.originalFailure,
|
|
45576
|
+
attempts: nextAttempt,
|
|
45577
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
|
|
45578
|
+
};
|
|
45579
|
+
this.settleCompletedTurn({
|
|
45580
|
+
flushDeferred: false,
|
|
45581
|
+
turnId: notification.turnId
|
|
45582
|
+
});
|
|
45583
|
+
this.scheduleFailedTurnRetry({
|
|
45584
|
+
attempt: nextAttempt,
|
|
45585
|
+
delayMs,
|
|
45586
|
+
failure,
|
|
45587
|
+
originalFailure: currentRetry.originalFailure
|
|
45588
|
+
});
|
|
45589
|
+
}
|
|
45590
|
+
holdRetryableError(notification, input) {
|
|
45591
|
+
this.clearPendingRetryableError();
|
|
45592
|
+
this.input.writeOutput?.(
|
|
45593
|
+
`agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
|
|
45594
|
+
);
|
|
45595
|
+
const timer = setTimeout(() => {
|
|
45596
|
+
if (this.pendingRetryableError?.notification !== notification) {
|
|
45597
|
+
return;
|
|
45598
|
+
}
|
|
45599
|
+
this.pendingRetryableError = null;
|
|
45600
|
+
if (!input.releaseOnTimeout) {
|
|
45601
|
+
return;
|
|
45602
|
+
}
|
|
45603
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45604
|
+
}, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
|
|
45605
|
+
timer.unref?.();
|
|
45606
|
+
this.pendingRetryableError = { notification, timer };
|
|
45607
|
+
}
|
|
45608
|
+
clearPendingRetryableError() {
|
|
45609
|
+
if (!this.pendingRetryableError) {
|
|
45610
|
+
return;
|
|
45611
|
+
}
|
|
45612
|
+
clearTimeout(this.pendingRetryableError.timer);
|
|
45613
|
+
this.pendingRetryableError = null;
|
|
45614
|
+
}
|
|
45615
|
+
scheduleFailedTurnRetry(input) {
|
|
45616
|
+
this.input.writeOutput?.(
|
|
45617
|
+
`agent_bridge_codex_turn_retry_scheduled attempt=${input.attempt} max_attempts=${MAX_CODEX_TURN_RETRY_ATTEMPTS} delay_ms=${input.delayMs} cause=${input.failure.cause} error=${singleLine(input.failure.message)}`
|
|
45618
|
+
);
|
|
45619
|
+
const timer = setTimeout(() => {
|
|
45620
|
+
void this.retryFailedTurn(input).catch((error51) => {
|
|
45621
|
+
this.enqueueCallback(() => this.input.onError(error51));
|
|
45622
|
+
});
|
|
45623
|
+
}, input.delayMs);
|
|
45624
|
+
timer.unref?.();
|
|
45625
|
+
}
|
|
45626
|
+
async retryFailedTurn(input) {
|
|
45627
|
+
if (this.closed) {
|
|
45628
|
+
return;
|
|
45629
|
+
}
|
|
45630
|
+
const threadId = this.threadId;
|
|
45631
|
+
if (threadId === null) {
|
|
45632
|
+
this.turnRetry = null;
|
|
45633
|
+
this.clearPendingRetryableError();
|
|
45634
|
+
this.flushDeferredMessages();
|
|
45635
|
+
this.enqueueCallback(
|
|
45636
|
+
() => this.input.onNotification(input.originalFailure)
|
|
45637
|
+
);
|
|
45638
|
+
return;
|
|
45639
|
+
}
|
|
45640
|
+
try {
|
|
45641
|
+
this.input.writeOutput?.(
|
|
45642
|
+
`agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
|
|
45643
|
+
);
|
|
45644
|
+
await this.startContinuationTurn(threadId);
|
|
45645
|
+
} catch (error51) {
|
|
45646
|
+
this.input.writeOutput?.(
|
|
45647
|
+
`agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
|
|
45648
|
+
);
|
|
45649
|
+
this.turnRetry = null;
|
|
45650
|
+
this.clearPendingRetryableError();
|
|
45651
|
+
this.flushDeferredMessages();
|
|
45652
|
+
this.enqueueCallback(
|
|
45653
|
+
() => this.input.onNotification(input.originalFailure)
|
|
45654
|
+
);
|
|
45655
|
+
}
|
|
45656
|
+
}
|
|
45366
45657
|
// Track turn/item lifecycle so steer/interrupt target the live turn and gate on
|
|
45367
45658
|
// tool-item settlement.
|
|
45368
45659
|
trackNotification(notification) {
|
|
@@ -45371,12 +45662,10 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45371
45662
|
this.activeTurnId = notification.turnId;
|
|
45372
45663
|
return;
|
|
45373
45664
|
case "turnCompleted":
|
|
45374
|
-
|
|
45375
|
-
|
|
45376
|
-
|
|
45377
|
-
|
|
45378
|
-
this.resolveSettlement();
|
|
45379
|
-
this.flushDeferredMessages();
|
|
45665
|
+
this.settleCompletedTurn({
|
|
45666
|
+
flushDeferred: true,
|
|
45667
|
+
turnId: notification.turnId
|
|
45668
|
+
});
|
|
45380
45669
|
return;
|
|
45381
45670
|
case "itemStarted":
|
|
45382
45671
|
if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
|
|
@@ -45393,6 +45682,16 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45393
45682
|
return;
|
|
45394
45683
|
}
|
|
45395
45684
|
}
|
|
45685
|
+
settleCompletedTurn(input) {
|
|
45686
|
+
if (input.turnId === this.activeTurnId) {
|
|
45687
|
+
this.activeTurnId = null;
|
|
45688
|
+
}
|
|
45689
|
+
this.pendingToolItemIds.clear();
|
|
45690
|
+
this.resolveSettlement();
|
|
45691
|
+
if (input.flushDeferred) {
|
|
45692
|
+
this.flushDeferredMessages();
|
|
45693
|
+
}
|
|
45694
|
+
}
|
|
45396
45695
|
onProcessExit(code) {
|
|
45397
45696
|
this.rejectAllPending(
|
|
45398
45697
|
new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
|
|
@@ -45489,6 +45788,9 @@ function threadIdFromResult(result) {
|
|
|
45489
45788
|
function errorMessage2(error51) {
|
|
45490
45789
|
return error51 instanceof Error ? error51.message : String(error51);
|
|
45491
45790
|
}
|
|
45791
|
+
function singleLine(message) {
|
|
45792
|
+
return message.replace(/\s+/g, " ").trim();
|
|
45793
|
+
}
|
|
45492
45794
|
|
|
45493
45795
|
// src/commands/agent-bridge/harness/codex/index.ts
|
|
45494
45796
|
function createCodexCommandHandler(input) {
|
|
@@ -49557,7 +49859,7 @@ function formatSearchText(result, writeLine, style) {
|
|
|
49557
49859
|
writeLine(
|
|
49558
49860
|
`${style.dim(String(match.sequence))} ${match.kind} ${style.label(
|
|
49559
49861
|
`[${snippet.query}]`
|
|
49560
|
-
)}: ${
|
|
49862
|
+
)}: ${singleLine2(snippet.text)}`
|
|
49561
49863
|
);
|
|
49562
49864
|
}
|
|
49563
49865
|
}
|
|
@@ -49719,16 +50021,16 @@ function entryPreview(event, full) {
|
|
|
49719
50021
|
return "";
|
|
49720
50022
|
}
|
|
49721
50023
|
});
|
|
49722
|
-
const joined =
|
|
50024
|
+
const joined = singleLine2(texts.join(" "));
|
|
49723
50025
|
return full ? joined : clip(joined);
|
|
49724
50026
|
}
|
|
49725
50027
|
function preview(value) {
|
|
49726
|
-
return clip(
|
|
50028
|
+
return clip(singleLine2(stringify5(value)));
|
|
49727
50029
|
}
|
|
49728
50030
|
function stringify5(value) {
|
|
49729
50031
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
49730
50032
|
}
|
|
49731
|
-
function
|
|
50033
|
+
function singleLine2(text) {
|
|
49732
50034
|
return text.replace(/\s+/g, " ").trim();
|
|
49733
50035
|
}
|
|
49734
50036
|
function clip(text) {
|