@autohq/cli 0.1.336 → 0.1.338
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 +297 -12
- package/dist/index.js +301 -16
- 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.338",
|
|
23438
23438
|
license: "SEE LICENSE IN README.md",
|
|
23439
23439
|
publishConfig: {
|
|
23440
23440
|
access: "public"
|
|
@@ -27535,7 +27535,11 @@ var SESSION_CHECK_STATUSES = [
|
|
|
27535
27535
|
"in_progress",
|
|
27536
27536
|
"completed"
|
|
27537
27537
|
];
|
|
27538
|
-
var SESSION_CHECK_CONCLUSIONS = [
|
|
27538
|
+
var SESSION_CHECK_CONCLUSIONS = [
|
|
27539
|
+
"success",
|
|
27540
|
+
"failure",
|
|
27541
|
+
"skipped"
|
|
27542
|
+
];
|
|
27539
27543
|
var SESSION_CHECK_TIMEOUT_PHASES = ["begin", "complete"];
|
|
27540
27544
|
var SessionCheckStatusSchema = external_exports.enum(SESSION_CHECK_STATUSES);
|
|
27541
27545
|
var SessionCheckConclusionSchema = external_exports.enum(SESSION_CHECK_CONCLUSIONS);
|
|
@@ -33554,6 +33558,19 @@ triggers:
|
|
|
33554
33558
|
content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\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/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n - event: github.pull_request.opened\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n - event: github.pull_request.reopened\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n - event: github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n'
|
|
33555
33559
|
}
|
|
33556
33560
|
]
|
|
33561
|
+
},
|
|
33562
|
+
{
|
|
33563
|
+
version: "1.3.0",
|
|
33564
|
+
files: [
|
|
33565
|
+
{
|
|
33566
|
+
path: "fragments/pr-review-slack.yaml",
|
|
33567
|
+
content: 'imports:\n - ./pr-review.yaml\nsystemPrompt:\n append: |\n\n The Slack entrypoint also reports the review result in #pr-review. Treat\n that Slack reply as a required output for this entrypoint.\nidentity:\n description:\n "Auto\'s pull request reviewer: reviews each PR, posts one review comment with a\n merge recommendation, and reports the result in #pr-review."\ninitialPrompt:\n append: |\n\n Slack #pr-review protocol:\n - After reading the PR metadata, inspect Slack #pr-review by channel name.\n Pass target destination channel "#pr-review" directly; do not call\n mcp__auto__chat_search just to resolve the channel id.\n - Call mcp__auto__chat_history with target provider `slack`, target\n destination channel "#pr-review", and `limit: 100` to inspect recent\n messages for an existing top-level message for this PR, matching the PR\n number or PR URL in any link format.\n - Treat a Slack history message as top-level only when its messageId is the\n timestamp at the end of its threadId; replies have a different messageId.\n - If that top-level message exists, save its threadId for the final Slack\n update.\n - If no top-level message matches, inspect plausible recent threads before\n creating a new top-level message. Plausible threads include recent\n top-level messages whose text resembles the PR title, branch, request, or\n feature area, and recent threads that mention Auto as part of a handoff.\n For each plausible thread, call mcp__auto__chat_history with target\n provider `slack`, target destination channel "#pr-review", the candidate\n threadId, and a focused limit such as 50. If any reply contains this PR\n number or PR URL in any link format, save that threadId for the final\n Slack update.\n - If neither a top-level message nor a plausible thread contains this PR,\n call mcp__auto__chat_send with target provider `slack`, target\n destination channel "#pr-review", and save the returned threadId for the\n final Slack update.\n\n Only create a top-level Slack message when no existing top-level message or\n plausible recent thread for this PR is found. Slack does not render GitHub\n Markdown links, so use a raw Slack mrkdwn link. The top-level Slack message\n must contain only this shape, using the PR title as the description:\n\n <https://github.com/{{github.repository.fullName}}/pull/{{github.pullRequest.number}}|PR #{{github.pullRequest.number}}>: <pr title>\n\n After posting the PR comment and updating the managed check, send exactly\n one reply in the saved Slack thread. Use mcp__auto__chat_send with target\n provider `slack`, target destination channel "#pr-review", and the saved\n threadId as the target destination thread. Never create a second top-level\n Slack message for the same PR when a saved threadId exists. Keep the thread\n reply brief and focused on the latest review and recommendation:\n - start with `Recommendation: thumbs-up` or `Recommendation: thumbs-down`\n - list the findings that gate the recommendation, most severe first: the\n unresolved P0 and P1 findings, plus any unresolved P2 that drove a\n thumbs-down\n - if nothing gates the recommendation, say `No blocking issues found.`\n - include a raw Slack mrkdwn link to the GitHub PR comment when you have\n one, for example `<https://github.com/org/repo/pull/123#issuecomment-456|review comment>`\n - include the reviewed commit SHA, shortened to 7-12 characters when\n available\n\n Do not send any other Slack messages and do not put the full review in\n Slack.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: slack\n'
|
|
33568
|
+
},
|
|
33569
|
+
{
|
|
33570
|
+
path: "fragments/pr-review.yaml",
|
|
33571
|
+
content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\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 a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\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/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 update that one\n comment in place with upsert_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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call 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'
|
|
33572
|
+
}
|
|
33573
|
+
]
|
|
33557
33574
|
}
|
|
33558
33575
|
],
|
|
33559
33576
|
"@auto/research-loop": [
|
|
@@ -67355,6 +67372,98 @@ function tomlString(value2) {
|
|
|
67355
67372
|
return `"${value2.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
67356
67373
|
}
|
|
67357
67374
|
|
|
67375
|
+
// src/commands/agent-bridge/harness/codex/turn-retry.ts
|
|
67376
|
+
var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
|
|
67377
|
+
var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
|
|
67378
|
+
var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
|
|
67379
|
+
var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
|
|
67380
|
+
var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
|
|
67381
|
+
var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
|
|
67382
|
+
function classifyRetryableCodexTurnFailure(message) {
|
|
67383
|
+
if (!message) {
|
|
67384
|
+
return null;
|
|
67385
|
+
}
|
|
67386
|
+
const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
|
|
67387
|
+
const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
|
|
67388
|
+
if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
|
|
67389
|
+
return null;
|
|
67390
|
+
}
|
|
67391
|
+
return {
|
|
67392
|
+
cause: "rate_limit",
|
|
67393
|
+
message,
|
|
67394
|
+
retryAfterMs
|
|
67395
|
+
};
|
|
67396
|
+
}
|
|
67397
|
+
function codexTurnRetryDelayMs(input) {
|
|
67398
|
+
if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
|
|
67399
|
+
return null;
|
|
67400
|
+
}
|
|
67401
|
+
const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
|
|
67402
|
+
if (remainingDelayBudgetMs <= 0) {
|
|
67403
|
+
return null;
|
|
67404
|
+
}
|
|
67405
|
+
const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
|
|
67406
|
+
return delayMs <= remainingDelayBudgetMs ? delayMs : null;
|
|
67407
|
+
}
|
|
67408
|
+
function isRateLimitMessage(message) {
|
|
67409
|
+
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(
|
|
67410
|
+
message
|
|
67411
|
+
) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
|
|
67412
|
+
}
|
|
67413
|
+
function parseExplicitRetryAfterMs(message) {
|
|
67414
|
+
return parseDelayMs(
|
|
67415
|
+
message.match(
|
|
67416
|
+
/\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
67417
|
+
)
|
|
67418
|
+
);
|
|
67419
|
+
}
|
|
67420
|
+
function parseTryAgainDelayMs(message) {
|
|
67421
|
+
return parseDelayMs(
|
|
67422
|
+
message.match(
|
|
67423
|
+
/\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
67424
|
+
)
|
|
67425
|
+
);
|
|
67426
|
+
}
|
|
67427
|
+
function parseDelayMs(match) {
|
|
67428
|
+
if (!match) {
|
|
67429
|
+
return null;
|
|
67430
|
+
}
|
|
67431
|
+
const value2 = Number.parseFloat(match[1] ?? "");
|
|
67432
|
+
if (!Number.isFinite(value2) || value2 < 0) {
|
|
67433
|
+
return null;
|
|
67434
|
+
}
|
|
67435
|
+
switch ((match[2] ?? "s").toLowerCase()) {
|
|
67436
|
+
case "millisecond":
|
|
67437
|
+
case "milliseconds":
|
|
67438
|
+
case "msec":
|
|
67439
|
+
case "msecs":
|
|
67440
|
+
case "ms":
|
|
67441
|
+
return Math.round(value2);
|
|
67442
|
+
case "minute":
|
|
67443
|
+
case "minutes":
|
|
67444
|
+
case "min":
|
|
67445
|
+
case "mins":
|
|
67446
|
+
case "m":
|
|
67447
|
+
return Math.round(value2 * 6e4);
|
|
67448
|
+
default:
|
|
67449
|
+
return Math.round(value2 * 1e3);
|
|
67450
|
+
}
|
|
67451
|
+
}
|
|
67452
|
+
function boundedRetryAfterMs(delayMs) {
|
|
67453
|
+
return Math.min(
|
|
67454
|
+
Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
|
|
67455
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
67456
|
+
);
|
|
67457
|
+
}
|
|
67458
|
+
function jitteredBackoffMs(attempt) {
|
|
67459
|
+
const baseDelayMs = Math.min(
|
|
67460
|
+
CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
|
|
67461
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
67462
|
+
);
|
|
67463
|
+
const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
|
|
67464
|
+
return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
|
|
67465
|
+
}
|
|
67466
|
+
|
|
67358
67467
|
// src/commands/agent-bridge/harness/codex/session.ts
|
|
67359
67468
|
var CODEX_REQUEST_TIMEOUT_MS = 3e4;
|
|
67360
67469
|
var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
|
|
@@ -67363,6 +67472,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
|
67363
67472
|
"fileChange",
|
|
67364
67473
|
"mcpToolCall"
|
|
67365
67474
|
]);
|
|
67475
|
+
var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
|
|
67366
67476
|
function startCodexAgentBridgeSession(input) {
|
|
67367
67477
|
return new CodexAgentBridgeSessionImpl(input);
|
|
67368
67478
|
}
|
|
@@ -67387,6 +67497,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67387
67497
|
// Messages held in "deferred" mode while a turn is in flight; flushed as a
|
|
67388
67498
|
// fresh turn once the active turn completes.
|
|
67389
67499
|
deferredMessages = [];
|
|
67500
|
+
turnRetry = null;
|
|
67501
|
+
pendingRetryableError = null;
|
|
67390
67502
|
constructor(input) {
|
|
67391
67503
|
this.input = input;
|
|
67392
67504
|
}
|
|
@@ -67397,6 +67509,13 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67397
67509
|
await this.ensureStarted();
|
|
67398
67510
|
const threadId = this.requireThreadId();
|
|
67399
67511
|
const mode = options?.mode ?? "interrupt";
|
|
67512
|
+
if (this.turnRetry !== null) {
|
|
67513
|
+
this.deferredMessages.push(message);
|
|
67514
|
+
this.input.writeOutput?.(
|
|
67515
|
+
"agent_bridge_codex_message_deferred reason=turn_retry"
|
|
67516
|
+
);
|
|
67517
|
+
return;
|
|
67518
|
+
}
|
|
67400
67519
|
if (this.activeTurnId === null) {
|
|
67401
67520
|
await this.startTurn(threadId, message);
|
|
67402
67521
|
return;
|
|
@@ -67432,6 +67551,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67432
67551
|
});
|
|
67433
67552
|
}
|
|
67434
67553
|
this.activeTurnId = null;
|
|
67554
|
+
this.turnRetry = null;
|
|
67555
|
+
this.clearPendingRetryableError();
|
|
67435
67556
|
this.pendingToolItemIds.clear();
|
|
67436
67557
|
this.resolveSettlement();
|
|
67437
67558
|
this.rejectAllPending(new Error("Codex session is closed"));
|
|
@@ -67534,6 +67655,12 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67534
67655
|
input: [userTextInput(message)]
|
|
67535
67656
|
});
|
|
67536
67657
|
}
|
|
67658
|
+
async startContinuationTurn(threadId) {
|
|
67659
|
+
await this.request("turn/start", {
|
|
67660
|
+
threadId,
|
|
67661
|
+
input: []
|
|
67662
|
+
});
|
|
67663
|
+
}
|
|
67537
67664
|
// Inject a message into the active turn. Prefers `turn/steer` (codex folds the
|
|
67538
67665
|
// input into the running turn and owns transcript consistency); falls back to a
|
|
67539
67666
|
// hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
|
|
@@ -67654,10 +67781,7 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67654
67781
|
this.settleResponse(message.id, message.result, message.error);
|
|
67655
67782
|
return;
|
|
67656
67783
|
case "notification":
|
|
67657
|
-
this.
|
|
67658
|
-
this.enqueueCallback(
|
|
67659
|
-
() => this.input.onNotification(message.notification)
|
|
67660
|
-
);
|
|
67784
|
+
this.handleNotification(message.notification);
|
|
67661
67785
|
return;
|
|
67662
67786
|
case "serverRequest":
|
|
67663
67787
|
this.enqueueCallback(() => this.input.onServerRequest(message.request));
|
|
@@ -67698,6 +67822,156 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67698
67822
|
}
|
|
67699
67823
|
});
|
|
67700
67824
|
}
|
|
67825
|
+
handleNotification(notification) {
|
|
67826
|
+
if (notification.type === "turnCompleted") {
|
|
67827
|
+
this.handleTurnCompletedNotification(notification);
|
|
67828
|
+
return;
|
|
67829
|
+
}
|
|
67830
|
+
if (notification.type === "error") {
|
|
67831
|
+
this.handleErrorNotification(notification);
|
|
67832
|
+
return;
|
|
67833
|
+
}
|
|
67834
|
+
this.trackNotification(notification);
|
|
67835
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67836
|
+
}
|
|
67837
|
+
handleErrorNotification(notification) {
|
|
67838
|
+
const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
|
|
67839
|
+
if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
|
|
67840
|
+
this.holdRetryableError(notification, {
|
|
67841
|
+
releaseOnTimeout: this.turnRetry === null
|
|
67842
|
+
});
|
|
67843
|
+
return;
|
|
67844
|
+
}
|
|
67845
|
+
this.trackNotification(notification);
|
|
67846
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67847
|
+
}
|
|
67848
|
+
handleTurnCompletedNotification(notification) {
|
|
67849
|
+
if (notification.status !== "failed") {
|
|
67850
|
+
this.turnRetry = null;
|
|
67851
|
+
this.clearPendingRetryableError();
|
|
67852
|
+
this.trackNotification(notification);
|
|
67853
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67854
|
+
return;
|
|
67855
|
+
}
|
|
67856
|
+
const failure = classifyRetryableCodexTurnFailure(
|
|
67857
|
+
notification.errorMessage
|
|
67858
|
+
);
|
|
67859
|
+
if (!failure) {
|
|
67860
|
+
this.turnRetry = null;
|
|
67861
|
+
this.clearPendingRetryableError();
|
|
67862
|
+
this.trackNotification(notification);
|
|
67863
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67864
|
+
return;
|
|
67865
|
+
}
|
|
67866
|
+
this.clearPendingRetryableError();
|
|
67867
|
+
const currentRetry = this.turnRetry ?? {
|
|
67868
|
+
originalFailure: notification,
|
|
67869
|
+
attempts: 0,
|
|
67870
|
+
cumulativeDelayMs: 0
|
|
67871
|
+
};
|
|
67872
|
+
const nextAttempt = currentRetry.attempts + 1;
|
|
67873
|
+
const delayMs = codexTurnRetryDelayMs({
|
|
67874
|
+
attempt: nextAttempt,
|
|
67875
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs,
|
|
67876
|
+
failure
|
|
67877
|
+
});
|
|
67878
|
+
if (delayMs === null) {
|
|
67879
|
+
this.turnRetry = null;
|
|
67880
|
+
this.settleCompletedTurn({
|
|
67881
|
+
flushDeferred: true,
|
|
67882
|
+
turnId: notification.turnId
|
|
67883
|
+
});
|
|
67884
|
+
this.input.writeOutput?.(
|
|
67885
|
+
`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")}`
|
|
67886
|
+
);
|
|
67887
|
+
this.enqueueCallback(
|
|
67888
|
+
() => this.input.onNotification(currentRetry.originalFailure)
|
|
67889
|
+
);
|
|
67890
|
+
return;
|
|
67891
|
+
}
|
|
67892
|
+
this.turnRetry = {
|
|
67893
|
+
originalFailure: currentRetry.originalFailure,
|
|
67894
|
+
attempts: nextAttempt,
|
|
67895
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
|
|
67896
|
+
};
|
|
67897
|
+
this.settleCompletedTurn({
|
|
67898
|
+
flushDeferred: false,
|
|
67899
|
+
turnId: notification.turnId
|
|
67900
|
+
});
|
|
67901
|
+
this.scheduleFailedTurnRetry({
|
|
67902
|
+
attempt: nextAttempt,
|
|
67903
|
+
delayMs,
|
|
67904
|
+
failure,
|
|
67905
|
+
originalFailure: currentRetry.originalFailure
|
|
67906
|
+
});
|
|
67907
|
+
}
|
|
67908
|
+
holdRetryableError(notification, input) {
|
|
67909
|
+
this.clearPendingRetryableError();
|
|
67910
|
+
this.input.writeOutput?.(
|
|
67911
|
+
`agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
|
|
67912
|
+
);
|
|
67913
|
+
const timer = setTimeout(() => {
|
|
67914
|
+
if (this.pendingRetryableError?.notification !== notification) {
|
|
67915
|
+
return;
|
|
67916
|
+
}
|
|
67917
|
+
this.pendingRetryableError = null;
|
|
67918
|
+
if (!input.releaseOnTimeout) {
|
|
67919
|
+
return;
|
|
67920
|
+
}
|
|
67921
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
67922
|
+
}, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
|
|
67923
|
+
timer.unref?.();
|
|
67924
|
+
this.pendingRetryableError = { notification, timer };
|
|
67925
|
+
}
|
|
67926
|
+
clearPendingRetryableError() {
|
|
67927
|
+
if (!this.pendingRetryableError) {
|
|
67928
|
+
return;
|
|
67929
|
+
}
|
|
67930
|
+
clearTimeout(this.pendingRetryableError.timer);
|
|
67931
|
+
this.pendingRetryableError = null;
|
|
67932
|
+
}
|
|
67933
|
+
scheduleFailedTurnRetry(input) {
|
|
67934
|
+
this.input.writeOutput?.(
|
|
67935
|
+
`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)}`
|
|
67936
|
+
);
|
|
67937
|
+
const timer = setTimeout(() => {
|
|
67938
|
+
void this.retryFailedTurn(input).catch((error51) => {
|
|
67939
|
+
this.enqueueCallback(() => this.input.onError(error51));
|
|
67940
|
+
});
|
|
67941
|
+
}, input.delayMs);
|
|
67942
|
+
timer.unref?.();
|
|
67943
|
+
}
|
|
67944
|
+
async retryFailedTurn(input) {
|
|
67945
|
+
if (this.closed) {
|
|
67946
|
+
return;
|
|
67947
|
+
}
|
|
67948
|
+
const threadId = this.threadId;
|
|
67949
|
+
if (threadId === null) {
|
|
67950
|
+
this.turnRetry = null;
|
|
67951
|
+
this.clearPendingRetryableError();
|
|
67952
|
+
this.flushDeferredMessages();
|
|
67953
|
+
this.enqueueCallback(
|
|
67954
|
+
() => this.input.onNotification(input.originalFailure)
|
|
67955
|
+
);
|
|
67956
|
+
return;
|
|
67957
|
+
}
|
|
67958
|
+
try {
|
|
67959
|
+
this.input.writeOutput?.(
|
|
67960
|
+
`agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
|
|
67961
|
+
);
|
|
67962
|
+
await this.startContinuationTurn(threadId);
|
|
67963
|
+
} catch (error51) {
|
|
67964
|
+
this.input.writeOutput?.(
|
|
67965
|
+
`agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
|
|
67966
|
+
);
|
|
67967
|
+
this.turnRetry = null;
|
|
67968
|
+
this.clearPendingRetryableError();
|
|
67969
|
+
this.flushDeferredMessages();
|
|
67970
|
+
this.enqueueCallback(
|
|
67971
|
+
() => this.input.onNotification(input.originalFailure)
|
|
67972
|
+
);
|
|
67973
|
+
}
|
|
67974
|
+
}
|
|
67701
67975
|
// Track turn/item lifecycle so steer/interrupt target the live turn and gate on
|
|
67702
67976
|
// tool-item settlement.
|
|
67703
67977
|
trackNotification(notification) {
|
|
@@ -67706,12 +67980,10 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67706
67980
|
this.activeTurnId = notification.turnId;
|
|
67707
67981
|
return;
|
|
67708
67982
|
case "turnCompleted":
|
|
67709
|
-
|
|
67710
|
-
|
|
67711
|
-
|
|
67712
|
-
|
|
67713
|
-
this.resolveSettlement();
|
|
67714
|
-
this.flushDeferredMessages();
|
|
67983
|
+
this.settleCompletedTurn({
|
|
67984
|
+
flushDeferred: true,
|
|
67985
|
+
turnId: notification.turnId
|
|
67986
|
+
});
|
|
67715
67987
|
return;
|
|
67716
67988
|
case "itemStarted":
|
|
67717
67989
|
if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
|
|
@@ -67728,6 +68000,16 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
67728
68000
|
return;
|
|
67729
68001
|
}
|
|
67730
68002
|
}
|
|
68003
|
+
settleCompletedTurn(input) {
|
|
68004
|
+
if (input.turnId === this.activeTurnId) {
|
|
68005
|
+
this.activeTurnId = null;
|
|
68006
|
+
}
|
|
68007
|
+
this.pendingToolItemIds.clear();
|
|
68008
|
+
this.resolveSettlement();
|
|
68009
|
+
if (input.flushDeferred) {
|
|
68010
|
+
this.flushDeferredMessages();
|
|
68011
|
+
}
|
|
68012
|
+
}
|
|
67731
68013
|
onProcessExit(code) {
|
|
67732
68014
|
this.rejectAllPending(
|
|
67733
68015
|
new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
|
|
@@ -67824,6 +68106,9 @@ function threadIdFromResult(result) {
|
|
|
67824
68106
|
function errorMessage2(error51) {
|
|
67825
68107
|
return error51 instanceof Error ? error51.message : String(error51);
|
|
67826
68108
|
}
|
|
68109
|
+
function singleLine(message) {
|
|
68110
|
+
return message.replace(/\s+/g, " ").trim();
|
|
68111
|
+
}
|
|
67827
68112
|
|
|
67828
68113
|
// src/commands/agent-bridge/harness/codex/index.ts
|
|
67829
68114
|
function createCodexCommandHandler(input) {
|
package/dist/index.js
CHANGED
|
@@ -19192,7 +19192,11 @@ var init_sessions = __esm({
|
|
|
19192
19192
|
"in_progress",
|
|
19193
19193
|
"completed"
|
|
19194
19194
|
];
|
|
19195
|
-
SESSION_CHECK_CONCLUSIONS = [
|
|
19195
|
+
SESSION_CHECK_CONCLUSIONS = [
|
|
19196
|
+
"success",
|
|
19197
|
+
"failure",
|
|
19198
|
+
"skipped"
|
|
19199
|
+
];
|
|
19196
19200
|
SESSION_CHECK_TIMEOUT_PHASES = ["begin", "complete"];
|
|
19197
19201
|
SessionCheckStatusSchema = external_exports.enum(SESSION_CHECK_STATUSES);
|
|
19198
19202
|
SessionCheckConclusionSchema = external_exports.enum(SESSION_CHECK_CONCLUSIONS);
|
|
@@ -25408,6 +25412,19 @@ triggers:
|
|
|
25408
25412
|
content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\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/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n - event: github.pull_request.opened\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n - event: github.pull_request.reopened\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n - event: github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result.\n beginTimeout:\n seconds: 1200\n conclusion: failure\n completeTimeout:\n seconds: 1200\n conclusion: failure\n routing:\n kind: spawn\n'
|
|
25409
25413
|
}
|
|
25410
25414
|
]
|
|
25415
|
+
},
|
|
25416
|
+
{
|
|
25417
|
+
version: "1.3.0",
|
|
25418
|
+
files: [
|
|
25419
|
+
{
|
|
25420
|
+
path: "fragments/pr-review-slack.yaml",
|
|
25421
|
+
content: 'imports:\n - ./pr-review.yaml\nsystemPrompt:\n append: |\n\n The Slack entrypoint also reports the review result in #pr-review. Treat\n that Slack reply as a required output for this entrypoint.\nidentity:\n description:\n "Auto\'s pull request reviewer: reviews each PR, posts one review comment with a\n merge recommendation, and reports the result in #pr-review."\ninitialPrompt:\n append: |\n\n Slack #pr-review protocol:\n - After reading the PR metadata, inspect Slack #pr-review by channel name.\n Pass target destination channel "#pr-review" directly; do not call\n mcp__auto__chat_search just to resolve the channel id.\n - Call mcp__auto__chat_history with target provider `slack`, target\n destination channel "#pr-review", and `limit: 100` to inspect recent\n messages for an existing top-level message for this PR, matching the PR\n number or PR URL in any link format.\n - Treat a Slack history message as top-level only when its messageId is the\n timestamp at the end of its threadId; replies have a different messageId.\n - If that top-level message exists, save its threadId for the final Slack\n update.\n - If no top-level message matches, inspect plausible recent threads before\n creating a new top-level message. Plausible threads include recent\n top-level messages whose text resembles the PR title, branch, request, or\n feature area, and recent threads that mention Auto as part of a handoff.\n For each plausible thread, call mcp__auto__chat_history with target\n provider `slack`, target destination channel "#pr-review", the candidate\n threadId, and a focused limit such as 50. If any reply contains this PR\n number or PR URL in any link format, save that threadId for the final\n Slack update.\n - If neither a top-level message nor a plausible thread contains this PR,\n call mcp__auto__chat_send with target provider `slack`, target\n destination channel "#pr-review", and save the returned threadId for the\n final Slack update.\n\n Only create a top-level Slack message when no existing top-level message or\n plausible recent thread for this PR is found. Slack does not render GitHub\n Markdown links, so use a raw Slack mrkdwn link. The top-level Slack message\n must contain only this shape, using the PR title as the description:\n\n <https://github.com/{{github.repository.fullName}}/pull/{{github.pullRequest.number}}|PR #{{github.pullRequest.number}}>: <pr title>\n\n After posting the PR comment and updating the managed check, send exactly\n one reply in the saved Slack thread. Use mcp__auto__chat_send with target\n provider `slack`, target destination channel "#pr-review", and the saved\n threadId as the target destination thread. Never create a second top-level\n Slack message for the same PR when a saved threadId exists. Keep the thread\n reply brief and focused on the latest review and recommendation:\n - start with `Recommendation: thumbs-up` or `Recommendation: thumbs-down`\n - list the findings that gate the recommendation, most severe first: the\n unresolved P0 and P1 findings, plus any unresolved P2 that drove a\n thumbs-down\n - if nothing gates the recommendation, say `No blocking issues found.`\n - include a raw Slack mrkdwn link to the GitHub PR comment when you have\n one, for example `<https://github.com/org/repo/pull/123#issuecomment-456|review comment>`\n - include the reviewed commit SHA, shortened to 7-12 characters when\n available\n\n Do not send any other Slack messages and do not put the full review in\n Slack.\ntools:\n chat:\n kind: local\n implementation: chat\n auth:\n kind: connection\n provider: slack\n connection: slack\n'
|
|
25422
|
+
},
|
|
25423
|
+
{
|
|
25424
|
+
path: "fragments/pr-review.yaml",
|
|
25425
|
+
content: 'model:\n provider: anthropic\n id: claude-opus-4-8\nlabels:\n purpose: pr-review\nsession:\n archiveAfterInactive:\n seconds: 86400\nsystemPrompt: |\n You are a code-analysis agent for Auto. Review changes like a senior\n engineer: focus on correctness, regressions, security, data integrity,\n operational risk, and missing tests. Keep output concise, concrete, and\n grounded in the diff. Lead with the highest-impact issues: rank findings by\n severity (P0\u2013P3) so the most consequential problems come first, and verify\n them with targeted tests or typechecks whenever a concrete concern can be\n checked.\n\n Also enforce the repository idioms documented in AGENTS.md and\n docs/idioms.md. Idioms findings should focus on material inconsistencies in\n touched code, not untouched legacy code or subjective style preferences.\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 a check with it), the managed check has been rolled\n onto the new head, and you re-begin the check and re-review against the\n pull request\'s current head. Keep exactly one current verdict per pull\n request at all times.\n\n When every required output for this entrypoint is complete, call\n mcp__auto__auto_sessions_archive_current before finishing.\nidentity:\n displayName: PR Review\n username: pr-review\n avatar:\n asset: .auto/assets/pr-reviewer.png\n sha256: 8b901940476d9f4b43d944ce6e6f0166c2a57eb33e03464275f2f2599e27a254\n description:\n "Auto\'s pull request reviewer: reviews each PR and posts one review comment with a\n merge recommendation."\ndisplayTitle: "Review PR #{{github.pullRequest.number}}: {{github.pullRequest.title}}"\ninitialPrompt: |\n Review GitHub pull request #{{github.pullRequest.number}} in {{github.repository.fullName}}.\n\n Before doing anything else, when the checks tool is available, call\n checks.begin with `{ "name": "pr-review" }`. This must happen before\n inspecting PR metadata or the diff.\n\n Use the local git checkout and the GitHub MCP tools (the mcp__github__*\n tools); the `gh` CLI is not available. Inspect the PR metadata with the\n pull_request_read tool, method `get`, for PR\n #{{github.pullRequest.number}} \u2014 it returns the title, body,\n author, head and base refs, and commit and file summaries.\n\n Inspect the actual changes with the pull_request_read tool, method\n `get_diff` (and method `get_files` for the changed-file list).\n\n Read AGENTS.md and docs/idioms.md before forming your recommendation. Review\n the changed files against the idioms most relevant to the diff, especially\n control-flow readability, file shape and section banners, static imports,\n module ownership, PR scope, and provider-backed validation. Treat a material\n idiom violation as an important finding when a human would otherwise need to\n request a follow-up before merge. Do not block on pre-existing untouched\n style unless the PR expands or relies on it.\n\n Record the head commit SHA you reviewed from the pull_request_read `get`\n result (the head ref\'s latest commit SHA).\n\n Determine whether you have reviewed this PR before. Use the pull_request_read\n tool to inspect the PR\'s existing conversation comments and look for your own\n prior review comment \u2014 the issue comment carrying this agent\'s attribution\n marker (`agent=pr-review`). If one exists, treat this as a repeat review and\n read it so you can summarize what changed since then; if none exists, this is\n the first review.\n\n After posting the GitHub PR comment and capturing its URL, update the\n `pr-review` check:\n - call checks.success when the PR comment\'s merge recommendation is\n "thumbs-up", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n - call checks.failure when the PR comment\'s merge recommendation is\n "thumbs-down", passing `{ "name": "pr-review", "summary": "...", "text": "..." }`\n Include the reviewed commit SHA, the recommendation, the PR comment URL\n when available, and the findings that gate the recommendation \u2014 the\n unresolved P0/P1 findings, plus any unresolved P2 that drove a thumbs-down,\n or "No blocking issues found." when nothing gates \u2014 in the check result.\n\n The local checkout is a shallow checkout of the PR head only. Do not assume\n origin/{{github.pullRequest.baseRef}} or origin/{{github.pullRequest.headRef}}\n exists locally unless you explicitly fetch it first.\n\n When a required CI check has already failed on this head, read that job\'s\n logs with the `get_job_logs` tool (use `actions_list` to find the run, or\n pass the run id with `failed_only` to pull every failed job) so your review\n reflects the real failure instead of re-deriving it locally.\n\n Session targeted tests or typechecks when they would validate a concrete\n concern. The checkout may not have node_modules installed yet. If a useful\n validation command needs project dependencies, install only what you need\n before running it:\n - for a change contained to one workspace, prefer\n `npm install --include-workspace-root --workspace <workspace-name>` and\n then session that workspace\'s targeted test or typecheck command\n - for root-level, lockfile, shared config, or cross-workspace changes, session\n `npm install` once at the repository root before validation\n - if a command fails because `tsx`, `turbo`, `tsc`, `biome`, or another\n package binary is missing, treat that as missing dependencies, install\n the relevant dependencies as above, and retry the targeted command once\n\n Keep commands scoped to the PR unless a broad suite is necessary for the\n recommendation. Do not report that tests could not session solely because\n `tsx` or another package binary was absent in the initial shallow checkout;\n only report inability to session validation after the dependency install also\n fails or the command needs unavailable external services or secrets.\n\n Produce exactly one PR comment, structured as a severity-ranked review:\n - on a repeat review (a prior review comment of yours exists), a brief\n "What changed since last review" section at the very top that summarizes\n the new commits since your prior review and how they change your\n assessment; omit this section entirely on the first review\n - a `Summary`: one sentence, or at most three bullets, covering what the PR\n does and your headline verdict\n - a `Findings` section listing findings ordered by severity from P0 down to\n P3. Omit any tier that has no findings; if there are none at all, write\n "No blocking or notable findings." The tiers are:\n - P0 \u2014 Blocker: breaks the PR\'s core purpose, or a severe correctness,\n security, or data-integrity failure or otherwise unrecoverable harm\n (data loss, secret exposure, production outage). Must fix before merge.\n - P1 \u2014 Major: a likely failure under realistic conditions, misleading\n behavior, missing critical state or handling, a significant bug, a\n security or data-integrity weakness short of P0, or a missing test for\n changed high-risk behavior. Should fix before merge.\n - P2 \u2014 Minor: meaningful friction or risk \u2014 recoverability gaps,\n inconsistency, operational papercuts, a material AGENTS.md/docs/idioms.md\n violation in touched code, or weaker-than-warranted test coverage. Fix\n or justify.\n - P3 \u2014 Nit: minor craft, consistency, or readability improvement. Optional.\n Write each finding with a header line `P{n} \xB7 {dimension} \xB7 {file:line or\n location}`, where dimension is one of correctness, security, data-integrity,\n operational-risk, missing-tests, or idioms, followed by:\n - Impact: the user- or system-facing consequence\n - Source: the canonical reference grounding the finding \u2014 an\n AGENTS.md/docs/idioms.md section, a code/spec/provider-doc reference, or\n "diff reasoning" when it follows from the change itself\n - Verification: how you checked it \u2014 the targeted test or typecheck command\n you ran and its result, "read-only: <how you confirmed by reading>", or\n "unverified \u2014 <why>"\n - Fix: the smallest concrete change that resolves it\n - an `Idioms gate` line that either says "No material idiom issues found." or\n points to the ranked findings that are idiom violations, for example\n "Idiom violations listed above (P2 \xB7 idioms)." Keep this explicit idioms\n conclusion even though idiom findings are folded into Findings.\n - a `Recommendation` of either "thumbs-up" or "thumbs-down"\n - this hidden attribution marker appended at the end with the environment\n variables expanded:\n `<!-- auto:v=1 session_id=$AUTO_SESSION_ID agent=$AUTO_AGENT_NAME -->`\n\n Decide the recommendation from the findings:\n - "thumbs-down" if any P0 or P1 finding is unresolved\n - "thumbs-down" if any P2 finding is unresolved, unless the PR body or author\n documents why it is acceptable for this change\n - P3 findings never gate the recommendation\n - otherwise "thumbs-up"\n\n Post the PR comment with the upsert_issue_comment tool. Pass the repository\n owner and name from {{github.repository.fullName}} as `owner` and `repo`, PR\n number {{github.pullRequest.number}} as `issueNumber`, and the full review as\n `body`. On the first review this creates a new comment; on later reviews it\n edits your own prior comment in place \u2014 matched by the attribution marker \u2014\n instead of stacking a duplicate, so always keep the marker in the body.\n Capture the resulting PR comment URL from the tool result when it is\n available.\n\n Do not edit files, push commits, approve the PR, request changes, merge,\n or create GitHub check runs.\nmounts:\n - kind: git\n repository: fractal-works/auto\n mountPath: /workspace/auto\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/auto\ntools:\n auto:\n kind: local\n implementation: auto\n github:\n kind: github\n tools:\n - pull_request_read\n - upsert_issue_comment\n # Read-only GitHub Actions tools so the review can read a failed CI\n # job\'s logs and ground its recommendation in the real failure instead\n # of re-deriving it locally. The mount already grants `actions: read`.\n - actions_get\n - actions_list\n - get_job_logs\ntriggers:\n # One reviewer session owns a PR across heads. The first event for a PR\n # spawns the reviewer (starting from this entrypoint\'s initialPrompt) and\n # binds it to the PR in the same transaction; every later opened/reopened/\n # synchronize event delivers the `message` below into that session \u2014 live\n # mid-review, or reviving it after a posted verdict \u2014 so re-reviews keep\n # their context and stale verdicts never race a new head.\n - name: pr-review\n events:\n - github.pull_request.opened\n - github.pull_request.reopened\n - github.pull_request.synchronize\n connection: github-fractal-works\n where:\n $.github.repository.fullName: fractal-works/auto\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 update that one\n comment in place with upsert_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 GitHub PR comment, call\n checks.success with { "name": "pr-review", "summary": "...",\n "text": "..." } only for a thumbs-up merge recommendation, and call\n checks.failure with { "name": "pr-review", "summary": "...",\n "text": "..." } for a thumbs-down merge recommendation. Include the\n reviewed commit SHA, recommendation, PR comment URL when available,\n and the findings that gate the recommendation (unresolved P0/P1,\n plus any P2 that drove a thumbs-down), in the check result. A\n delivered PR update rolls this check onto the new head and queues\n it again; call 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'
|
|
25426
|
+
}
|
|
25427
|
+
]
|
|
25411
25428
|
}
|
|
25412
25429
|
],
|
|
25413
25430
|
"@auto/research-loop": [
|
|
@@ -30438,7 +30455,7 @@ var init_package = __esm({
|
|
|
30438
30455
|
"package.json"() {
|
|
30439
30456
|
package_default = {
|
|
30440
30457
|
name: "@autohq/cli",
|
|
30441
|
-
version: "0.1.
|
|
30458
|
+
version: "0.1.338",
|
|
30442
30459
|
license: "SEE LICENSE IN README.md",
|
|
30443
30460
|
publishConfig: {
|
|
30444
30461
|
access: "public"
|
|
@@ -45003,6 +45020,98 @@ function tomlString(value) {
|
|
|
45003
45020
|
return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
|
|
45004
45021
|
}
|
|
45005
45022
|
|
|
45023
|
+
// src/commands/agent-bridge/harness/codex/turn-retry.ts
|
|
45024
|
+
var MAX_CODEX_TURN_RETRY_ATTEMPTS = 3;
|
|
45025
|
+
var MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS = 3e4;
|
|
45026
|
+
var CODEX_TURN_RETRY_BACKOFF_BASE_MS = 500;
|
|
45027
|
+
var CODEX_TURN_RETRY_MIN_DELAY_MS = 250;
|
|
45028
|
+
var CODEX_TURN_RETRY_MAX_DELAY_MS = 1e4;
|
|
45029
|
+
var CODEX_TURN_RETRY_JITTER_RATIO = 0.2;
|
|
45030
|
+
function classifyRetryableCodexTurnFailure(message) {
|
|
45031
|
+
if (!message) {
|
|
45032
|
+
return null;
|
|
45033
|
+
}
|
|
45034
|
+
const explicitRetryAfterMs = parseExplicitRetryAfterMs(message);
|
|
45035
|
+
const retryAfterMs = explicitRetryAfterMs ?? parseTryAgainDelayMs(message);
|
|
45036
|
+
if (!isRateLimitMessage(message) && explicitRetryAfterMs === null) {
|
|
45037
|
+
return null;
|
|
45038
|
+
}
|
|
45039
|
+
return {
|
|
45040
|
+
cause: "rate_limit",
|
|
45041
|
+
message,
|
|
45042
|
+
retryAfterMs
|
|
45043
|
+
};
|
|
45044
|
+
}
|
|
45045
|
+
function codexTurnRetryDelayMs(input) {
|
|
45046
|
+
if (input.attempt > MAX_CODEX_TURN_RETRY_ATTEMPTS) {
|
|
45047
|
+
return null;
|
|
45048
|
+
}
|
|
45049
|
+
const remainingDelayBudgetMs = MAX_CODEX_TURN_RETRY_TOTAL_DELAY_MS - input.cumulativeDelayMs;
|
|
45050
|
+
if (remainingDelayBudgetMs <= 0) {
|
|
45051
|
+
return null;
|
|
45052
|
+
}
|
|
45053
|
+
const delayMs = input.failure.retryAfterMs !== null ? boundedRetryAfterMs(input.failure.retryAfterMs) : jitteredBackoffMs(input.attempt);
|
|
45054
|
+
return delayMs <= remainingDelayBudgetMs ? delayMs : null;
|
|
45055
|
+
}
|
|
45056
|
+
function isRateLimitMessage(message) {
|
|
45057
|
+
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(
|
|
45058
|
+
message
|
|
45059
|
+
) || /\b429\b[^\n.]{0,80}\btoo many requests\b/i.test(message) || /\brate[-\s]?limit(?:ed| reached)?\b/i.test(message);
|
|
45060
|
+
}
|
|
45061
|
+
function parseExplicitRetryAfterMs(message) {
|
|
45062
|
+
return parseDelayMs(
|
|
45063
|
+
message.match(
|
|
45064
|
+
/\bretry[-\s]?after\b\s*[:=]?\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
45065
|
+
)
|
|
45066
|
+
);
|
|
45067
|
+
}
|
|
45068
|
+
function parseTryAgainDelayMs(message) {
|
|
45069
|
+
return parseDelayMs(
|
|
45070
|
+
message.match(
|
|
45071
|
+
/\b(?:try again|retry)\s+(?:in|after)\s+(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)?\b/i
|
|
45072
|
+
)
|
|
45073
|
+
);
|
|
45074
|
+
}
|
|
45075
|
+
function parseDelayMs(match) {
|
|
45076
|
+
if (!match) {
|
|
45077
|
+
return null;
|
|
45078
|
+
}
|
|
45079
|
+
const value = Number.parseFloat(match[1] ?? "");
|
|
45080
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
45081
|
+
return null;
|
|
45082
|
+
}
|
|
45083
|
+
switch ((match[2] ?? "s").toLowerCase()) {
|
|
45084
|
+
case "millisecond":
|
|
45085
|
+
case "milliseconds":
|
|
45086
|
+
case "msec":
|
|
45087
|
+
case "msecs":
|
|
45088
|
+
case "ms":
|
|
45089
|
+
return Math.round(value);
|
|
45090
|
+
case "minute":
|
|
45091
|
+
case "minutes":
|
|
45092
|
+
case "min":
|
|
45093
|
+
case "mins":
|
|
45094
|
+
case "m":
|
|
45095
|
+
return Math.round(value * 6e4);
|
|
45096
|
+
default:
|
|
45097
|
+
return Math.round(value * 1e3);
|
|
45098
|
+
}
|
|
45099
|
+
}
|
|
45100
|
+
function boundedRetryAfterMs(delayMs) {
|
|
45101
|
+
return Math.min(
|
|
45102
|
+
Math.max(delayMs, CODEX_TURN_RETRY_MIN_DELAY_MS),
|
|
45103
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
45104
|
+
);
|
|
45105
|
+
}
|
|
45106
|
+
function jitteredBackoffMs(attempt) {
|
|
45107
|
+
const baseDelayMs = Math.min(
|
|
45108
|
+
CODEX_TURN_RETRY_BACKOFF_BASE_MS * 2 ** (attempt - 1),
|
|
45109
|
+
CODEX_TURN_RETRY_MAX_DELAY_MS
|
|
45110
|
+
);
|
|
45111
|
+
const jitter = 1 - CODEX_TURN_RETRY_JITTER_RATIO + Math.random() * CODEX_TURN_RETRY_JITTER_RATIO * 2;
|
|
45112
|
+
return boundedRetryAfterMs(Math.round(baseDelayMs * jitter));
|
|
45113
|
+
}
|
|
45114
|
+
|
|
45006
45115
|
// src/commands/agent-bridge/harness/codex/session.ts
|
|
45007
45116
|
var CODEX_REQUEST_TIMEOUT_MS = 3e4;
|
|
45008
45117
|
var CODEX_ITEM_SETTLE_TIMEOUT_MS = 1e4;
|
|
@@ -45011,6 +45120,7 @@ var CODEX_TOOL_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
|
45011
45120
|
"fileChange",
|
|
45012
45121
|
"mcpToolCall"
|
|
45013
45122
|
]);
|
|
45123
|
+
var CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS = 1e3;
|
|
45014
45124
|
function startCodexAgentBridgeSession(input) {
|
|
45015
45125
|
return new CodexAgentBridgeSessionImpl(input);
|
|
45016
45126
|
}
|
|
@@ -45035,6 +45145,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45035
45145
|
// Messages held in "deferred" mode while a turn is in flight; flushed as a
|
|
45036
45146
|
// fresh turn once the active turn completes.
|
|
45037
45147
|
deferredMessages = [];
|
|
45148
|
+
turnRetry = null;
|
|
45149
|
+
pendingRetryableError = null;
|
|
45038
45150
|
constructor(input) {
|
|
45039
45151
|
this.input = input;
|
|
45040
45152
|
}
|
|
@@ -45045,6 +45157,13 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45045
45157
|
await this.ensureStarted();
|
|
45046
45158
|
const threadId = this.requireThreadId();
|
|
45047
45159
|
const mode = options?.mode ?? "interrupt";
|
|
45160
|
+
if (this.turnRetry !== null) {
|
|
45161
|
+
this.deferredMessages.push(message);
|
|
45162
|
+
this.input.writeOutput?.(
|
|
45163
|
+
"agent_bridge_codex_message_deferred reason=turn_retry"
|
|
45164
|
+
);
|
|
45165
|
+
return;
|
|
45166
|
+
}
|
|
45048
45167
|
if (this.activeTurnId === null) {
|
|
45049
45168
|
await this.startTurn(threadId, message);
|
|
45050
45169
|
return;
|
|
@@ -45080,6 +45199,8 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45080
45199
|
});
|
|
45081
45200
|
}
|
|
45082
45201
|
this.activeTurnId = null;
|
|
45202
|
+
this.turnRetry = null;
|
|
45203
|
+
this.clearPendingRetryableError();
|
|
45083
45204
|
this.pendingToolItemIds.clear();
|
|
45084
45205
|
this.resolveSettlement();
|
|
45085
45206
|
this.rejectAllPending(new Error("Codex session is closed"));
|
|
@@ -45182,6 +45303,12 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45182
45303
|
input: [userTextInput(message)]
|
|
45183
45304
|
});
|
|
45184
45305
|
}
|
|
45306
|
+
async startContinuationTurn(threadId) {
|
|
45307
|
+
await this.request("turn/start", {
|
|
45308
|
+
threadId,
|
|
45309
|
+
input: []
|
|
45310
|
+
});
|
|
45311
|
+
}
|
|
45185
45312
|
// Inject a message into the active turn. Prefers `turn/steer` (codex folds the
|
|
45186
45313
|
// input into the running turn and owns transcript consistency); falls back to a
|
|
45187
45314
|
// hard `turn/interrupt` + fresh `turn/start` when the turn cannot be steered.
|
|
@@ -45302,10 +45429,7 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45302
45429
|
this.settleResponse(message.id, message.result, message.error);
|
|
45303
45430
|
return;
|
|
45304
45431
|
case "notification":
|
|
45305
|
-
this.
|
|
45306
|
-
this.enqueueCallback(
|
|
45307
|
-
() => this.input.onNotification(message.notification)
|
|
45308
|
-
);
|
|
45432
|
+
this.handleNotification(message.notification);
|
|
45309
45433
|
return;
|
|
45310
45434
|
case "serverRequest":
|
|
45311
45435
|
this.enqueueCallback(() => this.input.onServerRequest(message.request));
|
|
@@ -45346,6 +45470,156 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45346
45470
|
}
|
|
45347
45471
|
});
|
|
45348
45472
|
}
|
|
45473
|
+
handleNotification(notification) {
|
|
45474
|
+
if (notification.type === "turnCompleted") {
|
|
45475
|
+
this.handleTurnCompletedNotification(notification);
|
|
45476
|
+
return;
|
|
45477
|
+
}
|
|
45478
|
+
if (notification.type === "error") {
|
|
45479
|
+
this.handleErrorNotification(notification);
|
|
45480
|
+
return;
|
|
45481
|
+
}
|
|
45482
|
+
this.trackNotification(notification);
|
|
45483
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45484
|
+
}
|
|
45485
|
+
handleErrorNotification(notification) {
|
|
45486
|
+
const retryableFailure = !notification.willRetry && classifyRetryableCodexTurnFailure(notification.message);
|
|
45487
|
+
if (retryableFailure && (this.activeTurnId !== null || this.turnRetry !== null)) {
|
|
45488
|
+
this.holdRetryableError(notification, {
|
|
45489
|
+
releaseOnTimeout: this.turnRetry === null
|
|
45490
|
+
});
|
|
45491
|
+
return;
|
|
45492
|
+
}
|
|
45493
|
+
this.trackNotification(notification);
|
|
45494
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45495
|
+
}
|
|
45496
|
+
handleTurnCompletedNotification(notification) {
|
|
45497
|
+
if (notification.status !== "failed") {
|
|
45498
|
+
this.turnRetry = null;
|
|
45499
|
+
this.clearPendingRetryableError();
|
|
45500
|
+
this.trackNotification(notification);
|
|
45501
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45502
|
+
return;
|
|
45503
|
+
}
|
|
45504
|
+
const failure = classifyRetryableCodexTurnFailure(
|
|
45505
|
+
notification.errorMessage
|
|
45506
|
+
);
|
|
45507
|
+
if (!failure) {
|
|
45508
|
+
this.turnRetry = null;
|
|
45509
|
+
this.clearPendingRetryableError();
|
|
45510
|
+
this.trackNotification(notification);
|
|
45511
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45512
|
+
return;
|
|
45513
|
+
}
|
|
45514
|
+
this.clearPendingRetryableError();
|
|
45515
|
+
const currentRetry = this.turnRetry ?? {
|
|
45516
|
+
originalFailure: notification,
|
|
45517
|
+
attempts: 0,
|
|
45518
|
+
cumulativeDelayMs: 0
|
|
45519
|
+
};
|
|
45520
|
+
const nextAttempt = currentRetry.attempts + 1;
|
|
45521
|
+
const delayMs = codexTurnRetryDelayMs({
|
|
45522
|
+
attempt: nextAttempt,
|
|
45523
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs,
|
|
45524
|
+
failure
|
|
45525
|
+
});
|
|
45526
|
+
if (delayMs === null) {
|
|
45527
|
+
this.turnRetry = null;
|
|
45528
|
+
this.settleCompletedTurn({
|
|
45529
|
+
flushDeferred: true,
|
|
45530
|
+
turnId: notification.turnId
|
|
45531
|
+
});
|
|
45532
|
+
this.input.writeOutput?.(
|
|
45533
|
+
`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")}`
|
|
45534
|
+
);
|
|
45535
|
+
this.enqueueCallback(
|
|
45536
|
+
() => this.input.onNotification(currentRetry.originalFailure)
|
|
45537
|
+
);
|
|
45538
|
+
return;
|
|
45539
|
+
}
|
|
45540
|
+
this.turnRetry = {
|
|
45541
|
+
originalFailure: currentRetry.originalFailure,
|
|
45542
|
+
attempts: nextAttempt,
|
|
45543
|
+
cumulativeDelayMs: currentRetry.cumulativeDelayMs + delayMs
|
|
45544
|
+
};
|
|
45545
|
+
this.settleCompletedTurn({
|
|
45546
|
+
flushDeferred: false,
|
|
45547
|
+
turnId: notification.turnId
|
|
45548
|
+
});
|
|
45549
|
+
this.scheduleFailedTurnRetry({
|
|
45550
|
+
attempt: nextAttempt,
|
|
45551
|
+
delayMs,
|
|
45552
|
+
failure,
|
|
45553
|
+
originalFailure: currentRetry.originalFailure
|
|
45554
|
+
});
|
|
45555
|
+
}
|
|
45556
|
+
holdRetryableError(notification, input) {
|
|
45557
|
+
this.clearPendingRetryableError();
|
|
45558
|
+
this.input.writeOutput?.(
|
|
45559
|
+
`agent_bridge_codex_retryable_error_held message=${singleLine(notification.message)}`
|
|
45560
|
+
);
|
|
45561
|
+
const timer = setTimeout(() => {
|
|
45562
|
+
if (this.pendingRetryableError?.notification !== notification) {
|
|
45563
|
+
return;
|
|
45564
|
+
}
|
|
45565
|
+
this.pendingRetryableError = null;
|
|
45566
|
+
if (!input.releaseOnTimeout) {
|
|
45567
|
+
return;
|
|
45568
|
+
}
|
|
45569
|
+
this.enqueueCallback(() => this.input.onNotification(notification));
|
|
45570
|
+
}, CODEX_RETRYABLE_ERROR_PAIRING_WINDOW_MS);
|
|
45571
|
+
timer.unref?.();
|
|
45572
|
+
this.pendingRetryableError = { notification, timer };
|
|
45573
|
+
}
|
|
45574
|
+
clearPendingRetryableError() {
|
|
45575
|
+
if (!this.pendingRetryableError) {
|
|
45576
|
+
return;
|
|
45577
|
+
}
|
|
45578
|
+
clearTimeout(this.pendingRetryableError.timer);
|
|
45579
|
+
this.pendingRetryableError = null;
|
|
45580
|
+
}
|
|
45581
|
+
scheduleFailedTurnRetry(input) {
|
|
45582
|
+
this.input.writeOutput?.(
|
|
45583
|
+
`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)}`
|
|
45584
|
+
);
|
|
45585
|
+
const timer = setTimeout(() => {
|
|
45586
|
+
void this.retryFailedTurn(input).catch((error51) => {
|
|
45587
|
+
this.enqueueCallback(() => this.input.onError(error51));
|
|
45588
|
+
});
|
|
45589
|
+
}, input.delayMs);
|
|
45590
|
+
timer.unref?.();
|
|
45591
|
+
}
|
|
45592
|
+
async retryFailedTurn(input) {
|
|
45593
|
+
if (this.closed) {
|
|
45594
|
+
return;
|
|
45595
|
+
}
|
|
45596
|
+
const threadId = this.threadId;
|
|
45597
|
+
if (threadId === null) {
|
|
45598
|
+
this.turnRetry = null;
|
|
45599
|
+
this.clearPendingRetryableError();
|
|
45600
|
+
this.flushDeferredMessages();
|
|
45601
|
+
this.enqueueCallback(
|
|
45602
|
+
() => this.input.onNotification(input.originalFailure)
|
|
45603
|
+
);
|
|
45604
|
+
return;
|
|
45605
|
+
}
|
|
45606
|
+
try {
|
|
45607
|
+
this.input.writeOutput?.(
|
|
45608
|
+
`agent_bridge_codex_turn_retry_starting attempt=${input.attempt} thread_id=${threadId}`
|
|
45609
|
+
);
|
|
45610
|
+
await this.startContinuationTurn(threadId);
|
|
45611
|
+
} catch (error51) {
|
|
45612
|
+
this.input.writeOutput?.(
|
|
45613
|
+
`agent_bridge_codex_turn_retry_failed attempt=${input.attempt} error=${errorMessage2(error51)}`
|
|
45614
|
+
);
|
|
45615
|
+
this.turnRetry = null;
|
|
45616
|
+
this.clearPendingRetryableError();
|
|
45617
|
+
this.flushDeferredMessages();
|
|
45618
|
+
this.enqueueCallback(
|
|
45619
|
+
() => this.input.onNotification(input.originalFailure)
|
|
45620
|
+
);
|
|
45621
|
+
}
|
|
45622
|
+
}
|
|
45349
45623
|
// Track turn/item lifecycle so steer/interrupt target the live turn and gate on
|
|
45350
45624
|
// tool-item settlement.
|
|
45351
45625
|
trackNotification(notification) {
|
|
@@ -45354,12 +45628,10 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45354
45628
|
this.activeTurnId = notification.turnId;
|
|
45355
45629
|
return;
|
|
45356
45630
|
case "turnCompleted":
|
|
45357
|
-
|
|
45358
|
-
|
|
45359
|
-
|
|
45360
|
-
|
|
45361
|
-
this.resolveSettlement();
|
|
45362
|
-
this.flushDeferredMessages();
|
|
45631
|
+
this.settleCompletedTurn({
|
|
45632
|
+
flushDeferred: true,
|
|
45633
|
+
turnId: notification.turnId
|
|
45634
|
+
});
|
|
45363
45635
|
return;
|
|
45364
45636
|
case "itemStarted":
|
|
45365
45637
|
if (CODEX_TOOL_ITEM_TYPES.has(notification.item.type)) {
|
|
@@ -45376,6 +45648,16 @@ var CodexAgentBridgeSessionImpl = class {
|
|
|
45376
45648
|
return;
|
|
45377
45649
|
}
|
|
45378
45650
|
}
|
|
45651
|
+
settleCompletedTurn(input) {
|
|
45652
|
+
if (input.turnId === this.activeTurnId) {
|
|
45653
|
+
this.activeTurnId = null;
|
|
45654
|
+
}
|
|
45655
|
+
this.pendingToolItemIds.clear();
|
|
45656
|
+
this.resolveSettlement();
|
|
45657
|
+
if (input.flushDeferred) {
|
|
45658
|
+
this.flushDeferredMessages();
|
|
45659
|
+
}
|
|
45660
|
+
}
|
|
45379
45661
|
onProcessExit(code) {
|
|
45380
45662
|
this.rejectAllPending(
|
|
45381
45663
|
new Error(`Codex app-server exited (code ${code ?? "unknown"})`)
|
|
@@ -45472,6 +45754,9 @@ function threadIdFromResult(result) {
|
|
|
45472
45754
|
function errorMessage2(error51) {
|
|
45473
45755
|
return error51 instanceof Error ? error51.message : String(error51);
|
|
45474
45756
|
}
|
|
45757
|
+
function singleLine(message) {
|
|
45758
|
+
return message.replace(/\s+/g, " ").trim();
|
|
45759
|
+
}
|
|
45475
45760
|
|
|
45476
45761
|
// src/commands/agent-bridge/harness/codex/index.ts
|
|
45477
45762
|
function createCodexCommandHandler(input) {
|
|
@@ -49540,7 +49825,7 @@ function formatSearchText(result, writeLine, style) {
|
|
|
49540
49825
|
writeLine(
|
|
49541
49826
|
`${style.dim(String(match.sequence))} ${match.kind} ${style.label(
|
|
49542
49827
|
`[${snippet.query}]`
|
|
49543
|
-
)}: ${
|
|
49828
|
+
)}: ${singleLine2(snippet.text)}`
|
|
49544
49829
|
);
|
|
49545
49830
|
}
|
|
49546
49831
|
}
|
|
@@ -49702,16 +49987,16 @@ function entryPreview(event, full) {
|
|
|
49702
49987
|
return "";
|
|
49703
49988
|
}
|
|
49704
49989
|
});
|
|
49705
|
-
const joined =
|
|
49990
|
+
const joined = singleLine2(texts.join(" "));
|
|
49706
49991
|
return full ? joined : clip(joined);
|
|
49707
49992
|
}
|
|
49708
49993
|
function preview(value) {
|
|
49709
|
-
return clip(
|
|
49994
|
+
return clip(singleLine2(stringify5(value)));
|
|
49710
49995
|
}
|
|
49711
49996
|
function stringify5(value) {
|
|
49712
49997
|
return typeof value === "string" ? value : JSON.stringify(value);
|
|
49713
49998
|
}
|
|
49714
|
-
function
|
|
49999
|
+
function singleLine2(text) {
|
|
49715
50000
|
return text.replace(/\s+/g, " ").trim();
|
|
49716
50001
|
}
|
|
49717
50002
|
function clip(text) {
|