chore: snapshot uncommitted local work before rebasing onto cloud-agent

Safety commit so the 94 uncommitted files in the working tree (in-flight
Account/Mac/Tip work plus the feature-preview recording tooling) survive the
move onto cursor/cloud-agent-1787824905651-vxuwc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 11:28:07 -07:00
co-authored by Claude Opus 5
parent 39002336c0
commit 5d51c5bf77
101 changed files with 3629 additions and 1354 deletions
@@ -126,6 +126,21 @@ final class ClipboardSemanticAnalyzerTests: XCTestCase {
XCTAssertFalse(acknowledgment.replyableMessage.isDetected)
}
/// The classifiers are trained on single sentences. A multi-sentence paste
/// analyzed as one blob dilutes bag-of-words confidence below the routing
/// threshold, so the intent must be scored per sentence instead.
func testIntentIsFoundInsideAMultiSentencePaste() async {
let analysis = await ClipboardSemanticAnalyzer().analyze(
"""
各位好,这是本周的进度同步。设计稿已经全部定稿,开发那边也开始联调了。\
麻烦你今天下班前把新版报价单发我。另外测试环境这两天可能会有波动,\
大家注意一下。
"""
)
XCTAssertTrue(analysis.task.isDetected)
}
func testPersonalPlanDoesNotBecomeAutomaticTask() async {
let analysis = await ClipboardSemanticAnalyzer().analyze(
"私人备忘:我准备周五自己整理完这份报告。"
@@ -369,6 +369,136 @@ final class ClipboardSkillSemanticRankerTests: XCTestCase {
)
}
func testLinkInsideAMessageKeepsTheMessageSkills() {
let recommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: "会议链接 https://zoom.us/j/123 周三下午三点开始,能来吗?",
analysis: analysis(
hasDate: true,
urls: [URL(string: "https://zoom.us/j/123")!],
question: detected(),
invitation: detected()
),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
XCTAssertEqual(
recommendations,
[
AIClipboardSkillCatalog.extractEventsID,
AIClipboardSkillCatalog.acceptInvitationID,
AIClipboardSkillCatalog.declineInvitationID,
AIClipboardSkillCatalog.replyID,
AIClipboardSkillCatalog.openLinkID
]
)
}
func testLabelledLinkPasteStaysExclusive() {
let recommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: "详情见 https://example.com/article",
analysis: analysis(urls: [URL(string: "https://example.com/article")!]),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
XCTAssertEqual(
recommendations,
[
AIClipboardSkillCatalog.openLinkID,
AIClipboardSkillCatalog.summarizeWebPageID,
AIClipboardSkillCatalog.replyID
]
)
}
func testPhoneNumberInsideAMessageKeepsTheMessageSkills() {
let recommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: "王经理说这个订单有问题,你直接打 400-666-8800 找售后处理一下。",
analysis: analysis(
phoneNumbers: [ClipboardTextLabel(sourceText: "400-666-8800")],
task: detected()
),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
XCTAssertEqual(
recommendations,
[
AIClipboardSkillCatalog.callPhoneID,
AIClipboardSkillCatalog.extractTodosID,
AIClipboardSkillCatalog.createContactID,
AIClipboardSkillCatalog.acceptTaskID,
AIClipboardSkillCatalog.replyID
]
)
}
/// 180 Chinese characters carry about as much text as 400 Latin ones, so the
/// summary threshold must fire well below the raw 360-character mark.
func testChineseTextReachesTheSummaryThresholdBelowTheLatinCharacterCount() {
let text = String(
repeating: "这是一段需要归纳整理的中文长文内容。",
count: 10
)
let recommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: text,
analysis: analysis(),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
XCTAssertLessThan(text.count, 360)
XCTAssertEqual(
recommendations,
[
AIClipboardSkillCatalog.summarizeID,
AIClipboardSkillCatalog.saveToNotesID,
AIClipboardSkillCatalog.replyID
]
)
}
func testHardWrappedChineseProseIsNotTreatedAsAList() {
let prose = [
"我们这次的目标是在下个季度之前完成整套流程的重构工作",
"同时还要保证现有的客户不会受到任何影响并保持稳定",
"最后需要在月底之前把完整的测试报告提交给管理层评审"
].joined(separator: "\n")
let proseRecommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: prose,
analysis: analysis(),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
let listRecommendations = ClipboardSkillSemanticRanker.recommended(
skills: AIClipboardSkillCatalog.catalog,
sourceText: "牛奶\n鸡蛋\n面包",
analysis: analysis(),
uiLanguage: .chinese,
limit: 5,
preferredLanguages: ["zh-Hans"]
).map(\.id)
XCTAssertEqual(proseRecommendations, [AIClipboardSkillCatalog.replyID])
XCTAssertTrue(
listRecommendations.contains(AIClipboardSkillCatalog.organizeListID)
)
}
private func rank(
text: String,
analysis: ClipboardSemanticAnalysis
+107
View File
@@ -0,0 +1,107 @@
// FlowLevelStoreTests.swift
// OSGKeyboardTests
//
// Regression cover for the waveform bar arithmetic. `calculateLevels` runs on
// the AVAudioEngine realtime thread, where a reversed `start..<end` range is an
// uncatchable Swift trap that takes the whole host app down mid-dictation — so
// the short-buffer cases below are the ones that matter, not the happy path.
import AVFoundation
@testable import OSGKeyboardHostSupport
import XCTest
final class FlowLevelStoreTests: XCTestCase {
/// Mirrors `FlowContinuousCapture.levelBarCount`, which cannot be read here
/// directly: it lives on a `@MainActor` type and this suite is nonisolated.
/// `testBarCountMatchesShippedConstant` keeps the two from drifting.
private let barCount = 24
@MainActor
func testBarCountMatchesShippedConstant() {
XCTAssertEqual(
barCount,
FlowContinuousCapture.levelBarCount,
"Bar count drifted — the short-buffer cases below are sized to it"
)
}
private func makeBuffer(frameLength: Int, amplitude: Float = 0.5) -> AVAudioPCMBuffer {
let format = AVAudioFormat(
commonFormat: .pcmFormatFloat32,
sampleRate: 48_000,
channels: 1,
interleaved: false
)!
// frameCapacity must be > 0 even when we want a zero-length buffer.
let buffer = AVAudioPCMBuffer(
pcmFormat: format,
frameCapacity: AVAudioFrameCount(max(frameLength, 1))
)!
buffer.frameLength = AVAudioFrameCount(frameLength)
if let channel = buffer.floatChannelData?[0] {
for index in 0..<frameLength {
channel[index] = amplitude
}
}
return buffer
}
/// The shipped bar count is 24. Any tap buffer shorter than that used to
/// drive the tail bars past `frameLength` and build a reversed range.
func testShortBuffersDoNotTrap() {
for frameLength in [0, 1, 2, 5, 10, 22, 23, 24, 25] {
let levels = FlowLevelStore.calculateLevels(
from: makeBuffer(frameLength: frameLength),
barCount: barCount
)
XCTAssertEqual(
levels.count,
barCount,
"frameLength=\(frameLength) must still produce a full bar array"
)
for level in levels {
XCTAssertFalse(level.isNaN, "frameLength=\(frameLength) produced NaN")
XCTAssertTrue(
(0...1).contains(level),
"frameLength=\(frameLength) produced out-of-range level \(level)"
)
}
}
}
/// Bars past the available samples read as silence rather than crashing.
func testBarsBeyondAvailableSamplesReadAsZero() {
let levels = FlowLevelStore.calculateLevels(
from: makeBuffer(frameLength: 10),
barCount: barCount
)
XCTAssertEqual(levels.count, barCount)
// 10 samples spread one-per-bar: the first 10 carry signal, the rest are empty.
XCTAssertTrue(levels[0] > 0, "leading bars should carry the signal")
for index in 10..<barCount {
XCTAssertEqual(levels[index], 0, "bar \(index) has no samples and must read 0")
}
}
/// Normal-sized tap buffers keep producing a full, non-silent waveform.
func testTypicalBufferSizesProduceSignal() {
for frameLength in [64, 441, 1_024, 4_096] {
let levels = FlowLevelStore.calculateLevels(
from: makeBuffer(frameLength: frameLength),
barCount: barCount
)
XCTAssertEqual(levels.count, barCount)
XCTAssertTrue(
levels.allSatisfy { $0 > 0 },
"frameLength=\(frameLength) should light every bar"
)
}
}
/// The instance path the audio tap actually calls.
func testUpdateFromShortBufferKeepsSnapshotUsable() {
let store = FlowLevelStore(barCount: barCount)
store.update(from: makeBuffer(frameLength: 3), barCount: barCount)
XCTAssertEqual(store.snapshot().count, barCount)
}
}
@@ -128,17 +128,29 @@ final class OfficialSkillCatalogTests: XCTestCase {
let migrated = store.agentSkillLayout
// What this test is named for: the v2 → current ladder installs the
// cached official transform skill, exactly once.
XCTAssertEqual(
migrated.enabledIDs,
[
AIClipboardSkillCatalog.replyID,
AIClipboardSkillCatalog.openLinkID,
AIClipboardSkillCatalog.summarizeWebPageID,
AIClipboardSkillCatalog.callPhoneID,
AIClipboardSkillCatalog.createContactID,
"official.rewrite"
]
migrated.enabledIDs.filter { $0 == "official.rewrite" }.count,
1,
"cached official transform skill must be installed exactly once"
)
// A layout the user already had must survive the migration intact.
XCTAssertEqual(migrated.enabledIDs.first, AIClipboardSkillCatalog.replyID)
XCTAssertEqual(
Set(migrated.enabledIDs).count,
migrated.enabledIDs.count,
"migration must not duplicate any skill"
)
// The "once" half: re-reading adds nothing further now that the stored
// migration version has caught up.
//
// Deliberately NOT asserting the full built-in list here. Which
// built-ins each ladder step adds is the ladder's own business (covered
// by the version-specific tests above); freezing the whole list in this
// test is exactly what rotted it when a later step was appended.
XCTAssertEqual(store.agentSkillLayout.enabledIDs, migrated.enabledIDs)
}
func testValidationRejectsWholeInvalidSnapshot() {
@@ -7,7 +7,7 @@ import Foundation
@testable import OSGKeyboardHostSupport
actor InMemoryAccountSecurityStore: AccountSessionVault, AppAttestKeyStateStoring {
private(set) var session: AccountSession?
private(set) var session: AccountTokenSession?
private(set) var refreshTransaction: AccountRefreshTransaction?
private(set) var keyState: AppAttestKeyState?
private(set) var clearSessionCount = 0
@@ -16,7 +16,7 @@ actor InMemoryAccountSecurityStore: AccountSessionVault, AppAttestKeyStateStorin
private var remainingSessionClearFailures: Int
init(
session: AccountSession? = nil,
session: AccountTokenSession? = nil,
refreshTransaction: AccountRefreshTransaction? = nil,
keyState: AppAttestKeyState? = nil,
sessionSaveFailures: Int = 0,
@@ -29,11 +29,11 @@ actor InMemoryAccountSecurityStore: AccountSessionVault, AppAttestKeyStateStorin
self.remainingSessionClearFailures = sessionClearFailures
}
func loadSession() async throws -> AccountSession? {
func loadSession() async throws -> AccountTokenSession? {
session
}
func saveSession(_ session: AccountSession) async throws {
func saveSession(_ session: AccountTokenSession) async throws {
if remainingSessionSaveFailures > 0 {
remainingSessionSaveFailures -= 1
throw AccountAPIError.secureStorage
@@ -107,13 +107,13 @@ actor QueueAccountTransport: AccountHTTPTransport {
}
actor RefreshMergingTransport: AccountHTTPTransport {
private let replacementSession: AccountSession
private let replacementSession: AccountTokenSession
private let refreshError: QueueAccountTransport.Stub?
private(set) var requests: [URLRequest] = []
private(set) var refreshCount = 0
init(
replacementSession: AccountSession,
replacementSession: AccountTokenSession,
refreshError: QueueAccountTransport.Stub? = nil
) {
self.replacementSession = replacementSession
@@ -248,7 +248,7 @@ func makeHTTPResponse(request: URLRequest, statusCode: Int) -> HTTPURLResponse {
)!
}
func sessionEnvelopeData(_ session: AccountSession) throws -> Data {
func sessionEnvelopeData(_ session: AccountTokenSession) throws -> Data {
try JSONEncoder().encode(APIDataEnvelope(data: session))
}
@@ -261,8 +261,8 @@ func makeAccountSession(
refreshToken: String = "refresh-old",
accessExpiry: Int64 = 4_000_000_000,
refreshExpiry: Int64 = 4_100_000_000
) -> AccountSession {
AccountSession(
) -> AccountTokenSession {
AccountTokenSession(
accountId: UUID(uuidString: "11111111-2222-3333-4444-555555555555")!,
tokenType: "Bearer",
accessToken: accessToken,
-39
View File
@@ -1,39 +0,0 @@
// TipProductTests.swift
// OSGKeyboardTests
//
// Locks tip product identifiers and optional support-count persistence.
@testable import OSGKeyboardShared
import XCTest
final class TipProductTests: XCTestCase {
private var suiteName: String!
private var defaults: UserDefaults!
override func setUp() {
super.setUp()
suiteName = "tip.tests.\(UUID().uuidString)"
defaults = UserDefaults(suiteName: suiteName)!
defaults.removePersistentDomain(forName: suiteName)
}
override func tearDown() {
defaults.removePersistentDomain(forName: suiteName)
defaults = nil
super.tearDown()
}
func testSupportProductIDIsStable() {
XCTAssertEqual(TipProduct.supportID, "ByRockyACoffee")
XCTAssertEqual(TipProduct.allProductIDs, [TipProduct.supportID])
}
func testSupportCountDefaultsKeyIsStable() {
XCTAssertEqual(TipProduct.supportCountDefaultsKey, "tipSupportPurchaseCount")
}
func testSupportCountPersistsInUserDefaults() {
defaults.set(2, forKey: TipProduct.supportCountDefaultsKey)
XCTAssertEqual(defaults.integer(forKey: TipProduct.supportCountDefaultsKey), 2)
}
}