From dbef8faa398ef3dfaddecbefafada5ed2cf3b9d1 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sun, 31 May 2026 05:13:38 -0700 Subject: [PATCH] perf: bound amp hook post queue Summary: - replace the generated Amp plugin unbounded promise-chain POST queue with a bounded async queue - keep hook callbacks non-blocking while capping retained status payloads during Orca downtime - update installer source assertions for bounded backlog behavior Validation: - pnpm exec vitest run src/main/amp/hook-service.test.ts src/main/agent-hooks/remote-hook-service-installers.test.ts src/main/agent-trust-presets.test.ts src/main/antigravity/hook-service.test.ts src/main/attribution/terminal-attribution.test.ts - pnpm run typecheck:node - pnpm lint - git diff --check - GitHub CI verify + track-community-pr passed --- src/main/amp/hook-service.test.ts | 5 ++++- src/main/amp/hook-service.ts | 32 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/src/main/amp/hook-service.test.ts b/src/main/amp/hook-service.test.ts index 9c83074d9..25c6939a1 100644 --- a/src/main/amp/hook-service.test.ts +++ b/src/main/amp/hook-service.test.ts @@ -50,10 +50,13 @@ describe('AmpHookService', () => { expect(source).toContain("amp.on('tool.result'") expect(source).toContain("amp.on('agent.end'") expect(source).toContain('return { action: "allow" }') - expect(source).toContain('let postQueue = Promise.resolve()') + expect(source).toContain('const MAX_PENDING_POSTS = 50') + expect(source).toContain('let postQueue: QueuedPost[] = []') expect(source).toContain('function enqueuePost') + expect(source).toContain('postQueue.shift()') expect(source).toContain('enqueuePost("tool.call"') expect(source).not.toContain('await post("tool.call"') + expect(source).not.toContain('postQueue = postQueue.then') expect(source).toContain('process.env.ORCA_PANE_KEY') expect(source).toContain('process.env.ORCA_AGENT_HOOK_ENDPOINT') }) diff --git a/src/main/amp/hook-service.ts b/src/main/amp/hook-service.ts index 93b0e066f..ad56b4641 100644 --- a/src/main/amp/hook-service.ts +++ b/src/main/amp/hook-service.ts @@ -247,11 +247,35 @@ function getAmpPluginSource(): string { ' }', '}', '', - 'let postQueue = Promise.resolve()', + 'const MAX_PENDING_POSTS = 50', + 'type QueuedPost = { hookEventName: string; payload: Record }', + 'let postQueue: QueuedPost[] = []', + 'let postDraining = false', + '', + 'async function drainPostQueue(): Promise {', + ' if (postDraining) return', + ' postDraining = true', + ' try {', + ' while (postQueue.length > 0) {', + ' const next = postQueue.shift()', + ' if (!next) continue', + ' await post(next.hookEventName, next.payload)', + ' }', + ' } finally {', + ' postDraining = false', + ' if (postQueue.length > 0) {', + ' void drainPostQueue()', + ' }', + ' }', + '}', 'function enqueuePost(hookEventName: string, payload: Record): void {', - ' // Why: keep hook callbacks non-blocking while preserving Amp event order.', - ' postQueue = postQueue.then(() => post(hookEventName, payload), () => post(hookEventName, payload))', - ' void postQueue.catch(() => {})', + ' // Why: keep hook callbacks non-blocking without retaining unbounded', + ' // payload closures when Orca is down and each POST waits for timeout.', + ' if (postQueue.length >= MAX_PENDING_POSTS) {', + ' postQueue.shift()', + ' }', + ' postQueue.push({ hookEventName, payload })', + ' void drainPostQueue()', '}', '', 'export default function (amp: PluginAPI) {',