diff --git a/bridge-browser/src/content/result_delivery_controller.ts b/bridge-browser/src/content/result_delivery_controller.ts index 50fc748..2ecfc05 100644 --- a/bridge-browser/src/content/result_delivery_controller.ts +++ b/bridge-browser/src/content/result_delivery_controller.ts @@ -38,7 +38,10 @@ export class ResultDeliveryController { } this.options.toolActivityTracker.updateDelivery(resultBatch.ids, "delivered"); - UI.triggerAutoSend({ autoSend: this.options.getAutoSend() }, selectors); + UI.triggerAutoSend({ + autoSend: this.options.getAutoSend(), + hasFileUpload: delivery.uploaded, + }, selectors); }) .catch((error: unknown) => { batchFinalized = true; diff --git a/bridge-browser/src/modules/auto_send.ts b/bridge-browser/src/modules/auto_send.ts index a8f8cbc..b065e94 100644 --- a/bridge-browser/src/modules/auto_send.ts +++ b/bridge-browser/src/modules/auto_send.ts @@ -9,20 +9,18 @@ import { } from "./page_selectors"; import { isElementVisible } from "./dom_helpers"; import { showUserAttentionNotification } from "./user_attention"; +import { getAutoSendAction, getAutoSendAttemptLimit } from "./auto_send_policy"; let autoSendTimer: NodeJS.Timeout | null = null; -type AutoSendAction = "ctrl-enter" | "enter" | "button"; const AUTO_SEND_INITIAL_DELAY_MS = 350; const AUTO_SEND_SETTLE_MS = 1200; const AUTO_SEND_RETRY_MS = 1600; -const AUTO_SEND_ACTIONS: AutoSendAction[] = [ - "enter", - "ctrl-enter", - "button", - "enter", - "ctrl-enter", -]; + +export interface AutoSendConfig { + autoSend: boolean; + hasFileUpload: boolean; +} /** * 终止当前正在进行的自动发送轮询机制 @@ -51,7 +49,7 @@ export function cancelAutoSend() { * - 4. 如果所有轮次还是失败,系统会弹出通知警告用户。 */ export function triggerAutoSend( - config: { autoSend: boolean }, + config: AutoSendConfig, domSelectors: SiteSelectors ) { if (!config.autoSend) {return;} @@ -61,7 +59,7 @@ export function triggerAutoSend( } let retryCount = 0; - const maxRetries = AUTO_SEND_ACTIONS.length; + const maxRetries = getAutoSendAttemptLimit(config.hasFileUpload); const getInputEl = () => getInputAreaElement(domSelectors); const getInputValue = (inputEl: HTMLElement): string => { @@ -107,7 +105,7 @@ export function triggerAutoSend( inputEl.dispatchEvent(new Event("change", { bubbles: true })); } - const action = AUTO_SEND_ACTIONS[retryCount] ?? "enter"; + const action = getAutoSendAction(retryCount); if (action === "ctrl-enter" || action === "enter") { if (inputEl) { const withCtrl = action === "ctrl-enter"; diff --git a/bridge-browser/src/modules/auto_send_policy.ts b/bridge-browser/src/modules/auto_send_policy.ts new file mode 100644 index 0000000..b8b2af1 --- /dev/null +++ b/bridge-browser/src/modules/auto_send_policy.ts @@ -0,0 +1,18 @@ +export type AutoSendAction = "ctrl-enter" | "enter" | "button"; + +const AUTO_SEND_ACTIONS: readonly AutoSendAction[] = [ + "enter", + "ctrl-enter", + "button", + "enter", + "ctrl-enter", +]; +const AUTO_SEND_FILE_ATTEMPTS = 20; + +export function getAutoSendAttemptLimit(hasFileUpload: boolean): number { + return hasFileUpload ? AUTO_SEND_FILE_ATTEMPTS : AUTO_SEND_ACTIONS.length; +} + +export function getAutoSendAction(attemptIndex: number): AutoSendAction { + return AUTO_SEND_ACTIONS[attemptIndex % AUTO_SEND_ACTIONS.length] ?? "enter"; +} diff --git a/bridge-browser/test/auto_send_policy.test.ts b/bridge-browser/test/auto_send_policy.test.ts new file mode 100644 index 0000000..feebd1e --- /dev/null +++ b/bridge-browser/test/auto_send_policy.test.ts @@ -0,0 +1,35 @@ +import { + getAutoSendAction, + getAutoSendAttemptLimit, +} from "../src/modules/auto_send_policy"; + +function main(): void { + runTest("keeps five attempts for text-only sends", () => { + assertEqual(getAutoSendAttemptLimit(false), 5, "text-only attempt limit changed"); + }); + runTest("allows twenty attempts after a file paste", () => { + assertEqual(getAutoSendAttemptLimit(true), 20, "file upload attempt limit was not extended"); + }); + runTest("repeats the fallback action sequence during extended retries", () => { + assertEqual(getAutoSendAction(2), "button", "initial button fallback changed"); + assertEqual(getAutoSendAction(7), "button", "extended retries did not repeat the button fallback"); + }); +} + +function runTest(name: string, test: () => void): void { + try { + test(); + console.log(`PASS ${name}`); + } catch (error) { + console.error(`FAIL ${name}`); + throw error; + } +} + +function assertEqual(actual: unknown, expected: unknown, message: string): void { + if (actual !== expected) { + throw new Error(`${message}: expected ${String(expected)}, received ${String(actual)}`); + } +} + +main(); diff --git a/bridge-browser/test/result_delivery.test.ts b/bridge-browser/test/result_delivery.test.ts index 29a9eac..6091943 100644 --- a/bridge-browser/test/result_delivery.test.ts +++ b/bridge-browser/test/result_delivery.test.ts @@ -16,6 +16,7 @@ async function main(): Promise { assertEqual(page.actions[2], "write", "result text was written before the attachment wait"); assert(page.input.innerText.includes('"status": "success"'), "acknowledged attachment became an error"); assert(delivery.delivered, "acknowledged result was not delivered"); + assert(delivery.uploaded, "dispatched attachment was not reported to auto-send"); }); await runTest("reports an unacknowledged paste for only its attachment result", async () => { const page = installFakePage(false); @@ -30,6 +31,7 @@ async function main(): Promise { assert(page.input.innerText.includes('"name": "read_file"'), "unrelated result was lost"); assert(page.input.innerText.includes('"output": "text result"'), "unrelated result was changed"); assert(delivery.delivered, "attachment failure text did not remain sendable"); + assert(delivery.uploaded, "unacknowledged attachment dispatch was not reported to auto-send"); }); await runTest("acknowledges each attachment group independently", async () => { const page = installFakePage([true, false]);