fix: keyboard stability, host-only analytics, and auto-reply dedupe

- record analytics/usage only in the host app; the extension no longer opens the
  SQLite queues, so hiding the keyboard cannot hold WAL locks (0xdead10cc)
- sync clipboard history writes across processes so deletes made on one side
  are not reverted by the other
- guard memory-relief reentrancy on controller and telemetry sides to break
  the recursive crash loop
- resolve credentials off the main thread and migrate legacy keys once per
  install, so Keychain reads cannot be caught in the suspend window
- gate Volcengine ASR ping completion to once (SIGTRAP on double resume)
- treat same-text captures as the same logical copy and dedupe auto actions
  by content fingerprint within 30s, stopping Universal Clipboard storms
- realign version line to 2.1.0 (build 110)
This commit is contained in:
2026-09-25 12:14:29 +08:00
parent c0848a00de
commit a2abfc5e8c
31 changed files with 727 additions and 314 deletions
@@ -126,4 +126,33 @@ final class ClipboardHistoryPolicyTests: XCTestCase {
)
XCTAssertEqual(tokens, ["great", "experience", "he", "writes"])
}
func testContentFingerprintIsStableAndTextSensitive() {
let first = ClipboardHistoryPolicy.contentFingerprint(for: "same message")
let second = ClipboardHistoryPolicy.contentFingerprint(for: "same message")
let other = ClipboardHistoryPolicy.contentFingerprint(for: "same message!")
XCTAssertEqual(first, second)
XCTAssertEqual(first.count, 64)
XCTAssertNotEqual(first, other)
}
func testRepeatSuppressionEndsAfterHintWindow() {
let firedAt = Date()
XCTAssertTrue(
ClipboardHistoryPolicy.isRepeatSuppressed(
firedAt: firedAt,
now: firedAt.addingTimeInterval(
ClipboardHistoryPolicy.aiHintEligibilitySeconds - 1
)
)
)
XCTAssertFalse(
ClipboardHistoryPolicy.isRepeatSuppressed(
firedAt: firedAt,
now: firedAt.addingTimeInterval(
ClipboardHistoryPolicy.aiHintEligibilitySeconds + 1
)
)
)
}
}