@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
@@ -0,0 +1,84 @@
1
+ // @bun
2
+ import {
3
+ ContractValidationError,
4
+ exports_external,
5
+ optionalWireMetadata,
6
+ recordingIDParser
7
+ } from "./hosted-v1-aavn7ktb.js";
8
+
9
+ // src/contracts/hosted-v1.ts
10
+ function parser(schema) {
11
+ return {
12
+ parse(value) {
13
+ try {
14
+ return schema.parse(value);
15
+ } catch {
16
+ throw new ContractValidationError;
17
+ }
18
+ },
19
+ safeParse(value) {
20
+ try {
21
+ return { success: true, data: schema.parse(value) };
22
+ } catch {
23
+ return { success: false, error: { code: "invalid_contract" } };
24
+ }
25
+ }
26
+ };
27
+ }
28
+ var json = exports_external.lazy(() => exports_external.union([exports_external.null(), exports_external.boolean(), exports_external.number().finite(), exports_external.string(), exports_external.array(json), exports_external.record(json)]));
29
+ var object = (shape) => exports_external.object(shape).catchall(json);
30
+ var id = exports_external.string().refine((value) => recordingIDParser.safeParse(value).success);
31
+ var timestamp = exports_external.string().datetime({ offset: true }).regex(/T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-](?:[01]\d|2[0-3]):[0-5]\d)$(?![\s\S])/);
32
+ var utcTimestamp = exports_external.string().datetime().regex(/T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$(?![\s\S])/);
33
+ var title = exports_external.string().trim().min(1).max(200);
34
+ var recordingInput = exports_external.object({ id, sessionId: id.optional(), title, transcript: exports_external.string().max(256000), durationMs: exports_external.number().finite().min(0).max(1800000) }).strict();
35
+ var recording = object({ ...recordingInput.shape, createdAt: timestamp, updatedAt: timestamp, provenance: exports_external.record(json).optional() });
36
+ var pasteInput = exports_external.object({
37
+ id,
38
+ recordingId: id.nullable().optional(),
39
+ text: exports_external.string().max(256000),
40
+ destinationAppId: exports_external.string().min(1).max(255).optional(),
41
+ destinationAppName: exports_external.string().min(1).max(200).optional(),
42
+ status: exports_external.enum(["attempted", "confirmed", "failed"]),
43
+ occurredAt: utcTimestamp.optional()
44
+ }).strict();
45
+ var receipt = object({
46
+ ...pasteInput.shape,
47
+ recordingId: id.nullable(),
48
+ occurredAt: timestamp,
49
+ createdAt: timestamp,
50
+ updatedAt: timestamp,
51
+ evidenceSource: exports_external.literal("client_reported")
52
+ });
53
+ var account = object({ id, displayName: exports_external.string(), email: exports_external.string(), createdAt: timestamp });
54
+ var advertised = { wireVersion: exports_external.string().optional(), capabilities: exports_external.array(exports_external.string()).max(32).optional() };
55
+ var compatible = (value) => {
56
+ try {
57
+ optionalWireMetadata(value);
58
+ return true;
59
+ } catch {
60
+ return false;
61
+ }
62
+ };
63
+ var recordingInputParser = parser(recordingInput);
64
+ var recordingParser = parser(recording);
65
+ var pasteInputParser = parser(pasteInput);
66
+ var pasteReceiptParser = parser(receipt);
67
+ var pageOptionsParser = parser(exports_external.object({
68
+ limit: exports_external.number().int().min(1).max(100).optional(),
69
+ before: timestamp.optional(),
70
+ beforeId: id.optional()
71
+ }).strict().refine((value) => value.beforeId === undefined === (value.before === undefined)));
72
+ var bootstrapInputParser = parser(exports_external.object({}).strict());
73
+ var renameInputParser = parser(exports_external.object({ title }).strict());
74
+ var accountResponseParser = parser(object({ account, ...advertised }).refine(compatible));
75
+ var versionResponseParser = parser(object({ name: exports_external.literal("recordings"), version: exports_external.string(), apiVersion: exports_external.literal("v1"), ...advertised }).refine(compatible));
76
+ var healthResponseParser = parser(object({ status: exports_external.literal("ok") }));
77
+ var readyResponseParser = parser(object({ status: exports_external.literal("ready") }));
78
+ var recordingResponseParser = parser(object({ recording }));
79
+ var recordingListParser = parser(object({ recordings: exports_external.array(recording).max(100) }));
80
+ var pasteResponseParser = parser(object({ receipt }));
81
+ var pasteListParser = parser(object({ receipts: exports_external.array(receipt).max(100) }));
82
+ var pendingDeletionParser = parser(object({ audioCleanup: object({ state: exports_external.literal("pending") }) }));
83
+
84
+ export { recordingInputParser, recordingParser, pasteInputParser, pasteReceiptParser, pageOptionsParser, bootstrapInputParser, renameInputParser, accountResponseParser, versionResponseParser, healthResponseParser, readyResponseParser, recordingResponseParser, recordingListParser, pasteResponseParser, pasteListParser, pendingDeletionParser };
package/dist/index.js CHANGED
@@ -2081,7 +2081,7 @@ function setAgentFocus(idOrName, projectId, db) {
2081
2081
  // package.json
2082
2082
  var package_default = {
2083
2083
  name: "@hasna/recordings",
2084
- version: "0.4.0",
2084
+ version: "0.5.0",
2085
2085
  type: "module",
2086
2086
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
2087
2087
  repository: {
@@ -2107,6 +2107,19 @@ var package_default = {
2107
2107
  "./sdk": {
2108
2108
  import: "./dist/sdk/index.js",
2109
2109
  types: "./dist/sdk/index.d.ts"
2110
+ },
2111
+ "./contracts/hosted-v1": {
2112
+ import: "./dist/contracts/hosted-v1.js",
2113
+ types: "./dist/contracts/hosted-v1.d.ts"
2114
+ },
2115
+ "./contracts/stream-v1": {
2116
+ import: "./dist/contracts/stream-v1.js",
2117
+ types: "./dist/contracts/stream-v1.d.ts"
2118
+ },
2119
+ "./contracts/fixtures/v1": "./contracts/v1/fixtures.json",
2120
+ "./hosted": {
2121
+ import: "./dist/hosted/index.js",
2122
+ types: "./dist/hosted/index.d.ts"
2110
2123
  }
2111
2124
  },
2112
2125
  engines: {
@@ -2118,17 +2131,17 @@ var package_default = {
2118
2131
  "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai --external=@aws-sdk/client-s3",
2119
2132
  "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai --external=@aws-sdk/client-s3",
2120
2133
  "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg --external=@aws-sdk/client-s3",
2121
- "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3",
2134
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3 && bun run build:contracts",
2122
2135
  "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
2123
2136
  "generate:sdk": "bun run scripts/generate-sdk.ts",
2124
2137
  migrate: "bun run scripts/migrate.ts",
2125
2138
  typecheck: "tsc --noEmit",
2126
2139
  "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
2127
- test: "bun test",
2140
+ test: "bun scripts/release-suite-gate.ts --all",
2128
2141
  "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
2129
2142
  "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
2130
2143
  "verify:ci-native": "bun scripts/ci-native-build.ts",
2131
- "test:gated": 'bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS="${RECORDINGS_TEST_TIMEOUT_MS:-120000}" bun test --timeout "${RECORDINGS_TEST_TIMEOUT_MS:-120000}" $(bun scripts/ci-linux-suite.ts --gated)',
2144
+ "test:gated": "bun scripts/release-suite-gate.ts",
2132
2145
  "test:vacuity-battery": 'bun scripts/vacuity-manifest-gen.ts > "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv" && bun scripts/vacuity-mutation-battery.ts "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv"',
2133
2146
  "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
2134
2147
  "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
@@ -2146,11 +2159,16 @@ var package_default = {
2146
2159
  "prepack:platform-gate": `bash -c 'if [ "$(uname -s)" = Darwin ]; then bun run build:native-fs-guard; else echo "WARN: native fs-guard build skipped on $(uname -s) \u2014 dry-run/CI pack; publish recordings from macOS"; fi'`,
2147
2160
  prepublishOnly: "bun run typecheck && bun run release-suite-gate",
2148
2161
  "release-suite-gate": "bun run scripts/release-suite-gate.ts",
2149
- "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
2162
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json",
2163
+ "build:contracts": "bun build src/contracts/hosted-v1.ts src/contracts/stream-v1.ts src/hosted/index.ts --target=bun --root src --outdir=dist --splitting --external=@hasna/contracts"
2150
2164
  },
2151
2165
  files: [
2152
2166
  "dist/",
2167
+ "!dist/__tests__/",
2153
2168
  "scripts/",
2169
+ "!scripts/test-recorder-fixtures.ts",
2170
+ "!scripts/**/__pycache__/",
2171
+ "!scripts/**/*.pyc",
2154
2172
  "Dockerfile.package",
2155
2173
  "bun.lock",
2156
2174
  "src/native/Recordings/App/",
@@ -2162,7 +2180,10 @@ var package_default = {
2162
2180
  "src/native/Recordings/build.sh",
2163
2181
  "packaging/macos/",
2164
2182
  "README.md",
2165
- "LICENSE"
2183
+ "LICENSE",
2184
+ "contracts/v1/fixtures.json",
2185
+ "docs/wire-contracts.md",
2186
+ "docs/hosted-sdk.md"
2166
2187
  ],
2167
2188
  dependencies: {
2168
2189
  "@aws-sdk/client-s3": "^3.1007.0",
package/dist/mcp/index.js CHANGED
@@ -5048,7 +5048,7 @@ function setAgentFocus(idOrName, projectId, db) {
5048
5048
  // package.json
5049
5049
  var package_default = {
5050
5050
  name: "@hasna/recordings",
5051
- version: "0.4.0",
5051
+ version: "0.5.0",
5052
5052
  type: "module",
5053
5053
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
5054
5054
  repository: {
@@ -5074,6 +5074,19 @@ var package_default = {
5074
5074
  "./sdk": {
5075
5075
  import: "./dist/sdk/index.js",
5076
5076
  types: "./dist/sdk/index.d.ts"
5077
+ },
5078
+ "./contracts/hosted-v1": {
5079
+ import: "./dist/contracts/hosted-v1.js",
5080
+ types: "./dist/contracts/hosted-v1.d.ts"
5081
+ },
5082
+ "./contracts/stream-v1": {
5083
+ import: "./dist/contracts/stream-v1.js",
5084
+ types: "./dist/contracts/stream-v1.d.ts"
5085
+ },
5086
+ "./contracts/fixtures/v1": "./contracts/v1/fixtures.json",
5087
+ "./hosted": {
5088
+ import: "./dist/hosted/index.js",
5089
+ types: "./dist/hosted/index.d.ts"
5077
5090
  }
5078
5091
  },
5079
5092
  engines: {
@@ -5085,17 +5098,17 @@ var package_default = {
5085
5098
  "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai --external=@aws-sdk/client-s3",
5086
5099
  "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai --external=@aws-sdk/client-s3",
5087
5100
  "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg --external=@aws-sdk/client-s3",
5088
- "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3",
5101
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3 && bun run build:contracts",
5089
5102
  "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
5090
5103
  "generate:sdk": "bun run scripts/generate-sdk.ts",
5091
5104
  migrate: "bun run scripts/migrate.ts",
5092
5105
  typecheck: "tsc --noEmit",
5093
5106
  "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
5094
- test: "bun test",
5107
+ test: "bun scripts/release-suite-gate.ts --all",
5095
5108
  "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
5096
5109
  "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
5097
5110
  "verify:ci-native": "bun scripts/ci-native-build.ts",
5098
- "test:gated": 'bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS="${RECORDINGS_TEST_TIMEOUT_MS:-120000}" bun test --timeout "${RECORDINGS_TEST_TIMEOUT_MS:-120000}" $(bun scripts/ci-linux-suite.ts --gated)',
5111
+ "test:gated": "bun scripts/release-suite-gate.ts",
5099
5112
  "test:vacuity-battery": 'bun scripts/vacuity-manifest-gen.ts > "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv" && bun scripts/vacuity-mutation-battery.ts "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv"',
5100
5113
  "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
5101
5114
  "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
@@ -5113,11 +5126,16 @@ var package_default = {
5113
5126
  "prepack:platform-gate": `bash -c 'if [ "$(uname -s)" = Darwin ]; then bun run build:native-fs-guard; else echo "WARN: native fs-guard build skipped on $(uname -s) \u2014 dry-run/CI pack; publish recordings from macOS"; fi'`,
5114
5127
  prepublishOnly: "bun run typecheck && bun run release-suite-gate",
5115
5128
  "release-suite-gate": "bun run scripts/release-suite-gate.ts",
5116
- "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
5129
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json",
5130
+ "build:contracts": "bun build src/contracts/hosted-v1.ts src/contracts/stream-v1.ts src/hosted/index.ts --target=bun --root src --outdir=dist --splitting --external=@hasna/contracts"
5117
5131
  },
5118
5132
  files: [
5119
5133
  "dist/",
5134
+ "!dist/__tests__/",
5120
5135
  "scripts/",
5136
+ "!scripts/test-recorder-fixtures.ts",
5137
+ "!scripts/**/__pycache__/",
5138
+ "!scripts/**/*.pyc",
5121
5139
  "Dockerfile.package",
5122
5140
  "bun.lock",
5123
5141
  "src/native/Recordings/App/",
@@ -5129,7 +5147,10 @@ var package_default = {
5129
5147
  "src/native/Recordings/build.sh",
5130
5148
  "packaging/macos/",
5131
5149
  "README.md",
5132
- "LICENSE"
5150
+ "LICENSE",
5151
+ "contracts/v1/fixtures.json",
5152
+ "docs/wire-contracts.md",
5153
+ "docs/hosted-sdk.md"
5133
5154
  ],
5134
5155
  dependencies: {
5135
5156
  "@aws-sdk/client-s3": "^3.1007.0",
@@ -21,7 +21,7 @@ var package_default;
21
21
  var init_package = __esm(() => {
22
22
  package_default = {
23
23
  name: "@hasna/recordings",
24
- version: "0.4.0",
24
+ version: "0.5.0",
25
25
  type: "module",
26
26
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
27
27
  repository: {
@@ -47,6 +47,19 @@ var init_package = __esm(() => {
47
47
  "./sdk": {
48
48
  import: "./dist/sdk/index.js",
49
49
  types: "./dist/sdk/index.d.ts"
50
+ },
51
+ "./contracts/hosted-v1": {
52
+ import: "./dist/contracts/hosted-v1.js",
53
+ types: "./dist/contracts/hosted-v1.d.ts"
54
+ },
55
+ "./contracts/stream-v1": {
56
+ import: "./dist/contracts/stream-v1.js",
57
+ types: "./dist/contracts/stream-v1.d.ts"
58
+ },
59
+ "./contracts/fixtures/v1": "./contracts/v1/fixtures.json",
60
+ "./hosted": {
61
+ import: "./dist/hosted/index.js",
62
+ types: "./dist/hosted/index.d.ts"
50
63
  }
51
64
  },
52
65
  engines: {
@@ -58,17 +71,17 @@ var init_package = __esm(() => {
58
71
  "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai --external=@aws-sdk/client-s3",
59
72
  "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai --external=@aws-sdk/client-s3",
60
73
  "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg --external=@aws-sdk/client-s3",
61
- "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3",
74
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3 && bun run build:contracts",
62
75
  "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
63
76
  "generate:sdk": "bun run scripts/generate-sdk.ts",
64
77
  migrate: "bun run scripts/migrate.ts",
65
78
  typecheck: "tsc --noEmit",
66
79
  "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
67
- test: "bun test",
80
+ test: "bun scripts/release-suite-gate.ts --all",
68
81
  "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
69
82
  "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
70
83
  "verify:ci-native": "bun scripts/ci-native-build.ts",
71
- "test:gated": 'bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS="${RECORDINGS_TEST_TIMEOUT_MS:-120000}" bun test --timeout "${RECORDINGS_TEST_TIMEOUT_MS:-120000}" $(bun scripts/ci-linux-suite.ts --gated)',
84
+ "test:gated": "bun scripts/release-suite-gate.ts",
72
85
  "test:vacuity-battery": 'bun scripts/vacuity-manifest-gen.ts > "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv" && bun scripts/vacuity-mutation-battery.ts "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv"',
73
86
  "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
74
87
  "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
@@ -86,11 +99,16 @@ var init_package = __esm(() => {
86
99
  "prepack:platform-gate": `bash -c 'if [ "$(uname -s)" = Darwin ]; then bun run build:native-fs-guard; else echo "WARN: native fs-guard build skipped on $(uname -s) \u2014 dry-run/CI pack; publish recordings from macOS"; fi'`,
87
100
  prepublishOnly: "bun run typecheck && bun run release-suite-gate",
88
101
  "release-suite-gate": "bun run scripts/release-suite-gate.ts",
89
- "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
102
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json",
103
+ "build:contracts": "bun build src/contracts/hosted-v1.ts src/contracts/stream-v1.ts src/hosted/index.ts --target=bun --root src --outdir=dist --splitting --external=@hasna/contracts"
90
104
  },
91
105
  files: [
92
106
  "dist/",
107
+ "!dist/__tests__/",
93
108
  "scripts/",
109
+ "!scripts/test-recorder-fixtures.ts",
110
+ "!scripts/**/__pycache__/",
111
+ "!scripts/**/*.pyc",
94
112
  "Dockerfile.package",
95
113
  "bun.lock",
96
114
  "src/native/Recordings/App/",
@@ -102,7 +120,10 @@ var init_package = __esm(() => {
102
120
  "src/native/Recordings/build.sh",
103
121
  "packaging/macos/",
104
122
  "README.md",
105
- "LICENSE"
123
+ "LICENSE",
124
+ "contracts/v1/fixtures.json",
125
+ "docs/wire-contracts.md",
126
+ "docs/hosted-sdk.md"
106
127
  ],
107
128
  dependencies: {
108
129
  "@aws-sdk/client-s3": "^3.1007.0",
package/dist/storage.js CHANGED
@@ -1013,7 +1013,7 @@ function setAgentFocus(idOrName, projectId, db) {
1013
1013
  // package.json
1014
1014
  var package_default = {
1015
1015
  name: "@hasna/recordings",
1016
- version: "0.4.0",
1016
+ version: "0.5.0",
1017
1017
  type: "module",
1018
1018
  description: "Speech-to-text recording tool with MCP and CLI \u2014 records, transcribes, and optionally enhances text using AI",
1019
1019
  repository: {
@@ -1039,6 +1039,19 @@ var package_default = {
1039
1039
  "./sdk": {
1040
1040
  import: "./dist/sdk/index.js",
1041
1041
  types: "./dist/sdk/index.d.ts"
1042
+ },
1043
+ "./contracts/hosted-v1": {
1044
+ import: "./dist/contracts/hosted-v1.js",
1045
+ types: "./dist/contracts/hosted-v1.d.ts"
1046
+ },
1047
+ "./contracts/stream-v1": {
1048
+ import: "./dist/contracts/stream-v1.js",
1049
+ types: "./dist/contracts/stream-v1.d.ts"
1050
+ },
1051
+ "./contracts/fixtures/v1": "./contracts/v1/fixtures.json",
1052
+ "./hosted": {
1053
+ import: "./dist/hosted/index.js",
1054
+ types: "./dist/hosted/index.d.ts"
1042
1055
  }
1043
1056
  },
1044
1057
  engines: {
@@ -1050,17 +1063,17 @@ var package_default = {
1050
1063
  "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai --external=@aws-sdk/client-s3",
1051
1064
  "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai --external=@aws-sdk/client-s3",
1052
1065
  "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg --external=@aws-sdk/client-s3",
1053
- "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3",
1066
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3 && bun run build:contracts",
1054
1067
  "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
1055
1068
  "generate:sdk": "bun run scripts/generate-sdk.ts",
1056
1069
  migrate: "bun run scripts/migrate.ts",
1057
1070
  typecheck: "tsc --noEmit",
1058
1071
  "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
1059
- test: "bun test",
1072
+ test: "bun scripts/release-suite-gate.ts --all",
1060
1073
  "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
1061
1074
  "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
1062
1075
  "verify:ci-native": "bun scripts/ci-native-build.ts",
1063
- "test:gated": 'bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS="${RECORDINGS_TEST_TIMEOUT_MS:-120000}" bun test --timeout "${RECORDINGS_TEST_TIMEOUT_MS:-120000}" $(bun scripts/ci-linux-suite.ts --gated)',
1076
+ "test:gated": "bun scripts/release-suite-gate.ts",
1064
1077
  "test:vacuity-battery": 'bun scripts/vacuity-manifest-gen.ts > "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv" && bun scripts/vacuity-mutation-battery.ts "${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv"',
1065
1078
  "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
1066
1079
  "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
@@ -1078,11 +1091,16 @@ var package_default = {
1078
1091
  "prepack:platform-gate": `bash -c 'if [ "$(uname -s)" = Darwin ]; then bun run build:native-fs-guard; else echo "WARN: native fs-guard build skipped on $(uname -s) \u2014 dry-run/CI pack; publish recordings from macOS"; fi'`,
1079
1092
  prepublishOnly: "bun run typecheck && bun run release-suite-gate",
1080
1093
  "release-suite-gate": "bun run scripts/release-suite-gate.ts",
1081
- "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
1094
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json",
1095
+ "build:contracts": "bun build src/contracts/hosted-v1.ts src/contracts/stream-v1.ts src/hosted/index.ts --target=bun --root src --outdir=dist --splitting --external=@hasna/contracts"
1082
1096
  },
1083
1097
  files: [
1084
1098
  "dist/",
1099
+ "!dist/__tests__/",
1085
1100
  "scripts/",
1101
+ "!scripts/test-recorder-fixtures.ts",
1102
+ "!scripts/**/__pycache__/",
1103
+ "!scripts/**/*.pyc",
1086
1104
  "Dockerfile.package",
1087
1105
  "bun.lock",
1088
1106
  "src/native/Recordings/App/",
@@ -1094,7 +1112,10 @@ var package_default = {
1094
1112
  "src/native/Recordings/build.sh",
1095
1113
  "packaging/macos/",
1096
1114
  "README.md",
1097
- "LICENSE"
1115
+ "LICENSE",
1116
+ "contracts/v1/fixtures.json",
1117
+ "docs/wire-contracts.md",
1118
+ "docs/hosted-sdk.md"
1098
1119
  ],
1099
1120
  dependencies: {
1100
1121
  "@aws-sdk/client-s3": "^3.1007.0",
@@ -0,0 +1,71 @@
1
+ # Hosted SDK
2
+
3
+ `@hasna/recordings/hosted` provides `HostedRecordingsClient` for hosted recording
4
+ metadata, account and paste-history operations. The existing `./sdk` retains its
5
+ separate legacy API. This additive client neither translates legacy fields nor
6
+ records audio, opens a login browser, manages credentials or starts a speech
7
+ provider. Bun is the tested runtime; browser compatibility is not claimed.
8
+
9
+ ```ts
10
+ import { HostedRecordingsClient, recordingCursor } from "@hasna/recordings/hosted";
11
+
12
+ const client = new HostedRecordingsClient({
13
+ apiBase: configuration.completeV1Base,
14
+ credentialProvider: ({ apiBase, signal }) => session.accessTokenFor(apiBase, signal),
15
+ });
16
+ const { recordings } = await client.listRecordings({ limit: 25 });
17
+ if (recordings.length === 25) {
18
+ const next = await client.listRecordings({ limit: 25, ...recordingCursor(recordings.at(-1)!) });
19
+ }
20
+ ```
21
+
22
+ The caller supplies a complete API base ending in `/v1`, including any prefix.
23
+ Remote bases require HTTPS; HTTP requires an explicit exact loopback address.
24
+ The client has no default endpoint or ambient environment, disk or Keychain
25
+ credential lookup. Public `health()` and `version()` never request credentials;
26
+ all other methods request a fresh caller-supplied Bearer credential. Injected
27
+ `fetch` is trusted and must honor manual redirects and AbortSignal. No request
28
+ follows a redirect, sends cookies or retries automatically.
29
+
30
+ `bootstrap()` sends an empty object; the server derives account/profile authority
31
+ from the authenticated session. `account()` reads that account and `ready()`
32
+ checks authenticated readiness. The caller owns OAuth, refresh and credential
33
+ storage. `logout()` revokes the current server session family; the caller must
34
+ also clear its stored credentials.
35
+
36
+ `listRecordings`, `getRecording`, `saveRecording` and `renameRecording` use
37
+ camelCase values: `id`, optional `sessionId`, `title`, `transcript`, and
38
+ `durationMs`. Duration is milliseconds. `deleteRecording` returns
39
+ `{state:'pending'}` for accepted cleanup or `{state:'removed'}` for completed
40
+ deletion. Pending is not proof that audio has been purged. Repeat explicitly;
41
+ reads may return the `recording_deleted` error after a permanent tombstone.
42
+
43
+ `listPasteReceipts`, `savePasteReceipt`, `deletePasteReceipt` and
44
+ `clearPasteHistory` use the hosted paste contract. An input `occurredAt` must be
45
+ UTC ending in `Z`; omit it for server time. `client_reported` receipt evidence
46
+ does not mean the server observed the target. Empty text remains valid when
47
+ retention is disabled. Recordings and receipts can contain private text; callers
48
+ should avoid logging responses.
49
+
50
+ Both lists require `before` and `beforeId` together or neither. `recordingCursor`
51
+ selects the last row's `createdAt` and ID; `pasteCursor` selects its `occurredAt`
52
+ and ID. There is no inferred total or automatic pagination. A full page can be
53
+ the last page. Inputs are validated before credentials or fetch; future JSON
54
+ response fields are preserved. Advertised metadata must include a compatible
55
+ version and all required capabilities; entirely absent metadata remains legacy
56
+ compatible. This is stricter than an older hosted adapter that accepted empty
57
+ capability advertisements.
58
+
59
+ The default 20-second deadline includes credential acquisition and the entire
60
+ response body. Caller cancellation has the same scope. Encoded JSON requests
61
+ are limited to 1 MiB before credentials are requested. Responses are limited to
62
+ 4 MiB of decoded bytes by default, configurable from 1 KiB to 32 MiB; compressed
63
+ wire Content-Length is not compared with decoded size. The transport bounds its
64
+ growing buffer, not total process memory. Error bodies are cancelled unread.
65
+
66
+ `RecordingsSDKError` contains a fixed code/message and optional HTTP status and
67
+ validated UUID request ID. It retains no body, endpoint, token, server message or
68
+ original cause. A timed-out mutation may already have committed: reconcile using
69
+ the known recording or receipt ID instead of blindly retrying. This JSON client
70
+ does not implement audio upload/download, WebSocket sessions, native app control,
71
+ tenant storage, usage admission or provider execution.
@@ -0,0 +1,24 @@
1
+ # Hosted recording wire contracts
2
+
3
+ The additive `@hasna/recordings/contracts/hosted-v1` and `@hasna/recordings/contracts/stream-v1` exports describe hosted recording metadata and PCM streaming. They perform no I/O, choose no endpoint, read no credentials and implement no provider or storage backend. Existing `./sdk`, CLI, MCP, serve, projects and native provider interfaces keep their existing behavior. The hosted camelCase API is distinct from the legacy SDK's snake_case API. Qualified timestamps require seconds; numeric offsets require a colon and valid hour/minute ranges.
4
+
5
+ ```ts
6
+ import { recordingInputParser } from "@hasna/recordings/contracts/hosted-v1";
7
+ import { streamEventParser } from "@hasna/recordings/contracts/stream-v1";
8
+
9
+ const request = recordingInputParser.parse(input);
10
+ const event = streamEventParser.safeParse(decodedEvent);
11
+ if (!event.success) handleInvalidContract(event.error.code);
12
+ ```
13
+
14
+ Every parser exposes an explicit `ContractParser<T>` interface with plain DTOs. `parse` throws a fixed `ContractValidationError`; `safeParse` returns `{success: true, data}` or `{success: false, error: {code: "invalid_contract"}}`. Neither error retains rejected data, schema-library internals or a cause. Consumers using different validation libraries can use these interfaces without exchanging Zod types.
15
+
16
+ Inputs reject unknown fields. Recording input has a UUID, title, transcript and duration in milliseconds; caller input cannot supply trusted provenance. Titles are trimmed. Paste input uses UTC timestamps ending in `Z`; output timestamps may have offsets. A paste receipt's `client_reported` evidence does not mean the server observed the target. Pagination requires `before` and `beforeId` together, or neither; either cursor component alone is invalid. Responses preserve extra JSON fields, including nested fields, while still checking known fields.
17
+
18
+ Hosted wire version `1.0` is independent of package and local-control versions. Metadata may be entirely absent for legacy v1. Once advertised, version and capabilities must both be present: major `1`, bounded minor, unique valid capability names, and all four required capabilities. Future compatible minors and additional capabilities are accepted. No incompatible advertisement downgrades to legacy mode. Bootstrap and version response parsers enforce these rules too. This intentionally tightens the previous hosted SDK's permissive metadata parsing to match service/native negotiation; it does not modify the legacy public SDK.
19
+
20
+ Streaming controls are strict. `parseStreamControlJSON` also enforces the existing 20,000-byte UTF-8 control-message ceiling. `pcmFrameParser` accepts only nonempty even-length PCM16 frames, from 2 through 24,000 bytes, without copying or converting them. The format is 24 kHz mono signed little-endian PCM16. Stream event parsers validate known event types, bounds, final capture identity and complete advertised metadata. New fields are allowed; unknown event types are rejected. Legacy final events may omit segments/provenance/recordingId, and legacy ready events may omit metadata/window fields.
21
+
22
+ These are value contracts, not a session state machine. The consumer must bind events to its current capture, require ready before sending, enforce sequence ordering, cumulative/buffer/deadline limits, require input finish before final delivery, validate renewal expiry against its clock, and require ready metadata when bootstrap advertised it. `acceptsAudioAcknowledgement` is a pure check against the consumer's sent and acknowledged byte counts; it does not update those counts. The consumer owns consent, authorization, cancellation and error display. Error-event messages are untrusted data and must not be logged as validation diagnostics.
23
+
24
+ Fictional positive and negative interoperability vectors are exported as `@hasna/recordings/contracts/fixtures/v1`. They freeze current input/output differences explicitly. Contract additions do not repoint or replace any legacy API. A subsequent reviewed package release and consumer adoption are required; introducing these modules does not publish them or change a deployed service.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/recordings",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "type": "module",
5
5
  "description": "Speech-to-text recording tool with MCP and CLI — records, transcribes, and optionally enhances text using AI",
6
6
  "repository": {
@@ -26,6 +26,19 @@
26
26
  "./sdk": {
27
27
  "import": "./dist/sdk/index.js",
28
28
  "types": "./dist/sdk/index.d.ts"
29
+ },
30
+ "./contracts/hosted-v1": {
31
+ "import": "./dist/contracts/hosted-v1.js",
32
+ "types": "./dist/contracts/hosted-v1.d.ts"
33
+ },
34
+ "./contracts/stream-v1": {
35
+ "import": "./dist/contracts/stream-v1.js",
36
+ "types": "./dist/contracts/stream-v1.d.ts"
37
+ },
38
+ "./contracts/fixtures/v1": "./contracts/v1/fixtures.json",
39
+ "./hosted": {
40
+ "import": "./dist/hosted/index.js",
41
+ "types": "./dist/hosted/index.d.ts"
29
42
  }
30
43
  },
31
44
  "engines": {
@@ -37,17 +50,17 @@
37
50
  "build:cli": "bun build src/cli/index.ts --target=bun --outfile=dist/cli/index.js --external=commander --external=chalk --external=openai --external=@aws-sdk/client-s3",
38
51
  "build:mcp": "bun build src/mcp/index.ts --target=bun --outfile=dist/mcp/index.js --external=@modelcontextprotocol/sdk --external=openai --external=@aws-sdk/client-s3",
39
52
  "build:serve": "bun build src/server/index.ts --target=bun --outfile=dist/server/index.js --external=@modelcontextprotocol/sdk --external=@hasna/contracts --external=openai --external=pg --external=@aws-sdk/client-s3",
40
- "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3",
53
+ "build:lib": "bun build src/index.ts src/storage.ts src/sdk/index.ts --target=bun --outdir=dist --external=openai --external=@aws-sdk/client-s3 && bun run build:contracts",
41
54
  "build:native-fs-guard": "/bin/bash scripts/build_native_fs_guard.sh",
42
55
  "generate:sdk": "bun run scripts/generate-sdk.ts",
43
56
  "migrate": "bun run scripts/migrate.ts",
44
57
  "typecheck": "tsc --noEmit",
45
58
  "typecheck:tcc-contract": "tsc --noEmit -p tsconfig.tcc-contract.json",
46
- "test": "bun test",
59
+ "test": "bun scripts/release-suite-gate.ts --all",
47
60
  "verify:ci-suite": "bun scripts/ci-linux-suite.ts --check",
48
61
  "verify:ci-suite:run": "bun scripts/ci-linux-suite.ts --verify-run",
49
62
  "verify:ci-native": "bun scripts/ci-native-build.ts",
50
- "test:gated": "bun scripts/ci-linux-suite.ts --check && RECORDINGS_TEST_TIMEOUT_MS=\"${RECORDINGS_TEST_TIMEOUT_MS:-120000}\" bun test --timeout \"${RECORDINGS_TEST_TIMEOUT_MS:-120000}\" $(bun scripts/ci-linux-suite.ts --gated)",
63
+ "test:gated": "bun scripts/release-suite-gate.ts",
51
64
  "test:vacuity-battery": "bun scripts/vacuity-manifest-gen.ts > \"${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv\" && bun scripts/vacuity-mutation-battery.ts \"${TMPDIR:-/tmp}/vacuity-corrupt-sites.tsv\"",
52
65
  "test:vacuity-battery:source": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/source-side.tsv",
53
66
  "test:vacuity-battery:chain": "bun scripts/vacuity-mutation-battery.ts scripts/vacuity-manifests/install-chain.tsv",
@@ -65,11 +78,16 @@
65
78
  "prepack:platform-gate": "bash -c 'if [ \"$(uname -s)\" = Darwin ]; then bun run build:native-fs-guard; else echo \"WARN: native fs-guard build skipped on $(uname -s) — dry-run/CI pack; publish recordings from macOS\"; fi'",
66
79
  "prepublishOnly": "bun run typecheck && bun run release-suite-gate",
67
80
  "release-suite-gate": "bun run scripts/release-suite-gate.ts",
68
- "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json"
81
+ "typecheck:shortcut-contract": "tsc --noEmit -p tsconfig.test.json",
82
+ "build:contracts": "bun build src/contracts/hosted-v1.ts src/contracts/stream-v1.ts src/hosted/index.ts --target=bun --root src --outdir=dist --splitting --external=@hasna/contracts"
69
83
  },
70
84
  "files": [
71
85
  "dist/",
86
+ "!dist/__tests__/",
72
87
  "scripts/",
88
+ "!scripts/test-recorder-fixtures.ts",
89
+ "!scripts/**/__pycache__/",
90
+ "!scripts/**/*.pyc",
73
91
  "Dockerfile.package",
74
92
  "bun.lock",
75
93
  "src/native/Recordings/App/",
@@ -81,7 +99,10 @@
81
99
  "src/native/Recordings/build.sh",
82
100
  "packaging/macos/",
83
101
  "README.md",
84
- "LICENSE"
102
+ "LICENSE",
103
+ "contracts/v1/fixtures.json",
104
+ "docs/wire-contracts.md",
105
+ "docs/hosted-sdk.md"
85
106
  ],
86
107
  "dependencies": {
87
108
  "@aws-sdk/client-s3": "^3.1007.0",