@hasna/recordings 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/README.md +56 -6
  2. package/contracts/v1/fixtures.json +1206 -0
  3. package/dist/cli/index.js +28 -7
  4. package/dist/contracts/hosted-v1.d.ts +105 -0
  5. package/dist/contracts/hosted-v1.d.ts.map +1 -0
  6. package/dist/contracts/hosted-v1.js +41 -0
  7. package/dist/contracts/stream-v1.d.ts +117 -0
  8. package/dist/contracts/stream-v1.d.ts.map +1 -0
  9. package/dist/contracts/stream-v1.js +35 -0
  10. package/dist/hosted/index.d.ts +58 -0
  11. package/dist/hosted/index.d.ts.map +1 -0
  12. package/dist/hosted/index.js +266 -0
  13. package/dist/hosted/transport.d.ts +36 -0
  14. package/dist/hosted/transport.d.ts.map +1 -0
  15. package/dist/hosted-v1-aavn7ktb.js +4114 -0
  16. package/dist/hosted-v1-gdr9extc.js +84 -0
  17. package/dist/index.js +27 -6
  18. package/dist/mcp/index.js +27 -6
  19. package/dist/server/index.js +27 -6
  20. package/dist/storage.js +27 -6
  21. package/docs/hosted-sdk.md +71 -0
  22. package/docs/wire-contracts.md +24 -0
  23. package/package.json +27 -6
  24. package/scripts/ci-linux-suite.ts +23 -13
  25. package/scripts/macos_artifact.ts +40 -33
  26. package/scripts/native/prebuilds/darwin-universal/recordings_fs_guard.node +0 -0
  27. package/scripts/native/recordings_fs_guard.c +36 -4
  28. package/scripts/native-core-receipt.py +171 -0
  29. package/scripts/native_fs_guard.ts +2 -0
  30. package/scripts/release-suite-gate.ts +227 -150
  31. package/scripts/resolve_tailscale_cli.sh +24 -3
  32. package/src/native/Recordings/RecordingsLib/BlockingOperation.swift +29 -0
  33. package/src/native/Recordings/RecordingsLib/Info.plist +2 -2
  34. package/src/native/Recordings/RecordingsLib/ProjectStore.swift +4 -4
  35. package/src/native/Recordings/RecordingsLib/RecordingEngine.swift +228 -60
  36. package/src/native/Recordings/RecordingsLib/RecordingPasteTarget.swift +114 -0
  37. package/src/native/Recordings/RecordingsLib/RecordingProvider.swift +13 -3
  38. package/src/native/Recordings/RecordingsTests/BlockingOperationTests.swift +85 -0
  39. package/src/native/Recordings/RecordingsTests/CLIRunnerTests.swift +441 -72
  40. package/src/native/Recordings/RecordingsTests/PipeClosureFixture.swift +215 -0
  41. package/src/native/Recordings/RecordingsTests/ProjectStoreTests.swift +10 -10
  42. package/src/native/Recordings/RecordingsTests/RecordingEngineDeliveryTests.swift +1 -1
  43. package/src/native/Recordings/RecordingsTests/RecordingFrozenPasteTargetTests.swift +50 -0
  44. package/src/native/Recordings/RecordingsTests/RecordingPasteTargetTrackerTests.swift +48 -0
  45. package/src/native/Recordings/RecordingsTests/RecordingProviderTests.swift +115 -2
  46. package/src/native/Recordings/RecordingsTests/RecordingStartTimingTests.swift +93 -21
  47. package/src/native/Recordings/RecordingsTests/TestHomeDirectory.swift +5 -1
  48. package/src/native/Recordings/build.sh +2 -1
  49. package/dist/__tests__/helpers/installer-guard-execution.d.ts +0 -22
  50. package/dist/__tests__/helpers/installer-guard-execution.d.ts.map +0 -1
  51. package/dist/__tests__/helpers/installer-preflight.d.ts +0 -22
  52. package/dist/__tests__/helpers/installer-preflight.d.ts.map +0 -1
  53. package/dist/__tests__/helpers/native-fs-guard.d.ts +0 -2
  54. package/dist/__tests__/helpers/native-fs-guard.d.ts.map +0 -1
  55. package/dist/__tests__/helpers/source-assertions.d.ts +0 -171
  56. package/dist/__tests__/helpers/source-assertions.d.ts.map +0 -1
  57. package/dist/__tests__/preload.d.ts +0 -2
  58. package/dist/__tests__/preload.d.ts.map +0 -1
@@ -75,11 +75,16 @@ public protocol RecordingTranscriptionProvider: Sendable {
75
75
  }
76
76
 
77
77
  public protocol RecordingTranscriptionSession: Sendable {
78
- /// Called in capture order, in roughly 100 ms chunks, off the audio callback.
78
+ /// Called in capture order with variable-sized admitted PCM packets, off the audio
79
+ /// callback. Empty packets are omitted; all admitted packets precede inputEnded().
79
80
  /// Return promptly. Providers must bound any queue while connecting or sending.
80
- /// The final short chunk is delivered before finish. An in-flight append may race cancel;
81
- /// a cancelled session must ignore it.
81
+ /// An in-flight append may race cancel; a cancelled session must ignore it.
82
82
  func appendPCM(_ data: Data)
83
+ /// Called once after all capture/converter PCM drains, before the fallback WAV is written.
84
+ /// Streaming providers may close input now and cache an early final result. Return promptly;
85
+ /// this does not authorize publishing text or reading audioURL before finish is called.
86
+ /// Must be thread-safe and idempotent, and ignore calls after cancellation.
87
+ func inputEnded()
83
88
  /// Called once after capture drains and the fallback WAV is safely written.
84
89
  /// File-based providers may ignore appendPCM and recognize this file locally.
85
90
  func finish(_ request: RecordingTranscriptionRequest) async throws -> RecordingProviderResult
@@ -87,6 +92,11 @@ public protocol RecordingTranscriptionSession: Sendable {
87
92
  func cancel()
88
93
  }
89
94
 
95
+ public extension RecordingTranscriptionSession {
96
+ /// File providers and existing adapters keep their finish-after-WAV behavior.
97
+ func inputEnded() {}
98
+ }
99
+
90
100
  public enum RecordingProviderError: Error, LocalizedError, Sendable {
91
101
  case invalidIsolation
92
102
  case noAudio
@@ -0,0 +1,85 @@
1
+ import Dispatch
2
+ import Foundation
3
+ import Testing
4
+ @testable import RecordingsLib
5
+
6
+ @Suite("Blocking operation scheduling")
7
+ struct BlockingOperationTests {
8
+ private final class Observation: @unchecked Sendable {
9
+ let entered = DispatchSemaphore(value: 0)
10
+ let release = DispatchSemaphore(value: 0)
11
+ let peer = DispatchSemaphore(value: 0)
12
+ private let lock = NSLock()
13
+ private var result = (entered: false, peerBeforeRelease: false)
14
+
15
+ func record(entered: Bool, peerBeforeRelease: Bool) {
16
+ lock.lock()
17
+ result = (entered, peerBeforeRelease)
18
+ lock.unlock()
19
+ }
20
+
21
+ func snapshot() -> (entered: Bool, peerBeforeRelease: Bool) {
22
+ lock.lock()
23
+ defer { lock.unlock() }
24
+ return result
25
+ }
26
+ }
27
+
28
+ @Test("blocking work leaves executor progress available and drains after cancellation", arguments: [false, true], [false, true])
29
+ func peerProgressWhileBlocked(cancel: Bool, delayedSubmission: Bool) async {
30
+ let observation = Observation()
31
+ // A single buffered handoff gives the operation its already-created Task handle
32
+ // without blocking a cooperative worker or capturing an uninitialized variable.
33
+ let handoff = AsyncStream<Task<(Int, Bool), Never>>.makeStream(bufferingPolicy: .bufferingNewest(1))
34
+ let work = Task.detached {
35
+ var handles = handoff.stream.makeAsyncIterator()
36
+ guard let ownTask = await handles.next() else {
37
+ preconditionFailure("Fixture task handle was not supplied")
38
+ }
39
+ // Admission is deliberately slower than the former two-second entry timer.
40
+ // Only progress while the synchronous operation is held is under observation.
41
+ if delayedSubmission { try? await Task.sleep(for: .seconds(3)) }
42
+ let value = await BlockingOperation.run {
43
+ observation.entered.signal()
44
+ // Start the observation inside the operation, after entry. This independent
45
+ // thread always releases the gate, even when the peer misses its deadline.
46
+ Thread.detachNewThread {
47
+ defer { observation.release.signal() }
48
+ let entered = observation.entered.wait(timeout: .now()) == .success
49
+ if cancel { ownTask.cancel() }
50
+ if entered {
51
+ Task.detached { observation.peer.signal() }
52
+ }
53
+ let progressed = entered && observation.peer.wait(timeout: .now() + .seconds(1)) == .success
54
+ observation.record(entered: entered, peerBeforeRelease: progressed)
55
+ }
56
+ observation.release.wait()
57
+ return 7
58
+ }
59
+ return (value, Task.isCancelled)
60
+ }
61
+ handoff.continuation.yield(work)
62
+ handoff.continuation.finish()
63
+ let (value, wasCancelled) = await work.value
64
+ let observed = observation.snapshot()
65
+ #expect(observed.entered)
66
+ #expect(observed.peerBeforeRelease)
67
+ #expect(value == 7)
68
+ #expect(wasCancelled == cancel)
69
+ }
70
+
71
+ private enum FixtureFailure: Error { case expected }
72
+
73
+ @Test("throwing blocking work preserves its exact error")
74
+ func preservesError() async {
75
+ do {
76
+ let _: Int = try await BlockingOperation.runThrowing(qos: .utility) {
77
+ throw FixtureFailure.expected
78
+ }
79
+ Issue.record("Blocking operation discarded its error")
80
+ } catch FixtureFailure.expected {
81
+ } catch {
82
+ Issue.record("Blocking operation replaced its error: \(error)")
83
+ }
84
+ }
85
+ }