fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: recover the original result on a duplicate approval decision - #123

Closed
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery
Closed

fix: recover the original result on a duplicate approval decision#123
vaibhav8a wants to merge 1 commit into
extra-org:mainfrom
vaibhav8a:fix/http-duplicate-approval-recovery

Conversation

@vaibhav8a

Copy link
Copy Markdown

Closes#109.

Problem

/approve, /reject and /decision all funnel through _decide in src/agent_engine/api/app.py, which mapped ApprovalAlreadyProcessed straight to a 409 with no recovery. A client that retries after a network timeout has no way to read 409 as "the decision you're re-sending already succeeded", so a successful approval surfaces as an error.

ConversationService.resume_run already handles this exact exception by recovering the original result. The HTTP layer was the inconsistent one.

Change

Catch ApprovalAlreadyProcessed ahead of the general ApprovalError handler and recover through engine.get_processed_result(...), mirroring the agent_manager precedent:

exceptApprovalAlreadyProcessedasexc:
recovered=awaitengine.get_processed_result(
run_id, approval_id,
caller_user_id=user_id,
caller_session_id=caller_session_id,
)
ifrecoveredisNone:
raise_map_approval_error(exc) fromexcresult=recovered

The 409 is still returned when the result is genuinely unavailable, so nothing that previously failed now silently succeeds. Every other ApprovalError keeps its existing mapping, and session-scoping is unchanged — get_processed_result receives the same caller_session_id that resume did (hoisted into a local so the two calls can't drift).

Testing

Added test_duplicate_approval_recovers_the_original_result in tests/api/test_approval_endpoints.py: it approves once, approves again, and asserts the second call returns 200 with the same status and answer as the first.

Confirmed it guards the change — with the app.py hunk stashed it fails on assert 409 == 200.

pytest tests/api tests/approvals # 134 passed
ruff check src/agent_engine/api/app.py tests/api/test_approval_endpoints.py # All checks passed

The /approve, /reject and /decision endpoints all funnel through _decide,
which mapped ApprovalAlreadyProcessed straight to a 409. A client retrying
after a network timeout has no way to read that as "the decision you are
re-sending already succeeded", so a successful approval surfaced as an
error.
Catch ApprovalAlreadyProcessed and recover via engine.get_processed_result,
exactly as ConversationService already does for the same exception, and
keep the 409 only when the result is genuinely unavailable.
Closes#109
@AmitAvital1

Copy link
Copy Markdown
Collaborator

Hi @vaibhav8a ! Thanks for contributing but this issue already was assigned to someone else, with already advance PR that merged (#110). so this issue has been close. For future things, please see inside the issue if have already open PR or if its assign to other person. this will omit the duplication working!
We will hope to see your contribution again on other issues.

For now i'm closing this PR.
Thanks

@vaibhav8a

Copy link
Copy Markdown
Author

Understood, and thanks for the clear steer — #110 was merged with the issue left open, so I picked it up off an open-issue sweep without checking what had already landed against it. Entirely my miss.

I've also closed my own #125 for the same reason: #86 has been open since 2 August with the same fix and a regression test.

For what it's worth, checking closedByPullRequestsReferences on the issue catches both shapes — a merged PR that left the issue open, and an open PR whose branch name doesn't mention the issue — which is what I'd been missing. I'll come back with something that isn't already covered.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP API doesn't recover from duplicate approval decision

2 participants

@vaibhav8a@AmitAvital1