@mycadet/assistant 0.12.11 → 0.13.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 (176) hide show
  1. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  2. package/node_modules/@mycadet/ces-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  3. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  4. package/node_modules/@mycadet/gateway-client/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  5. package/node_modules/@mycadet/gateway-client/src/gateway-ipc-contracts.ts +0 -20
  6. package/node_modules/@mycadet/service-contracts/src/__tests__/ingress.test.ts +2 -2
  7. package/node_modules/@mycadet/service-contracts/src/ingress.ts +2 -2
  8. package/openapi.yaml +2 -708
  9. package/package.json +2 -3
  10. package/scripts/sync-web-search-catalog.ts +1 -2
  11. package/src/__tests__/cloud-profile-guard.test.ts +2 -2
  12. package/src/__tests__/config-schema.test.ts +6 -38
  13. package/src/__tests__/conversation-queue.test.ts +45 -6
  14. package/src/__tests__/db-readiness-http-gate.test.ts +0 -6
  15. package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
  16. package/src/__tests__/persona-resolver.test.ts +0 -51
  17. package/src/__tests__/provider-cloud-proxy-integration.test.ts +2 -110
  18. package/src/__tests__/run-due-schedules.test.ts +0 -4
  19. package/src/__tests__/schedule-store.test.ts +0 -12
  20. package/src/__tests__/stt-stream-session.test.ts +4 -4
  21. package/src/__tests__/telephony-tts-guard.test.ts +2 -50
  22. package/src/__tests__/voice-config-update.test.ts +12 -167
  23. package/src/__tests__/web-search-catalog-parity.test.ts +0 -2
  24. package/src/__tests__/workspace-migration-130-speech-mode-to-provider.test.ts +9 -7
  25. package/src/__tests__/workspace-migration-132-web-search-mode-to-provider.test.ts +6 -6
  26. package/src/calls/__tests__/voice-session-bridge.test.ts +1 -4
  27. package/src/calls/resolve-call-tts-provider.ts +6 -25
  28. package/src/calls/telephony-credential-preflight.ts +0 -10
  29. package/src/calls/telephony-tts-capability.ts +1 -17
  30. package/src/cli/commands/tts.ts +1 -2
  31. package/src/cli/index.help.ts +0 -2
  32. package/src/cli/program.ts +0 -2
  33. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +4 -42
  34. package/src/config/schemas/__tests__/stt.test.ts +10 -14
  35. package/src/config/schemas/services.ts +5 -6
  36. package/src/config/schemas/stt.ts +1 -3
  37. package/src/config/schemas/tts.ts +1 -30
  38. package/src/daemon/daemon-readiness.ts +4 -29
  39. package/src/daemon/lifecycle.ts +0 -31
  40. package/src/daemon/providers-setup.ts +0 -8
  41. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  42. package/src/heartbeat/heartbeat-service.ts +1 -66
  43. package/src/ipc/__tests__/db-readiness-ipc-gate.test.ts +1 -9
  44. package/src/live-voice/__tests__/live-voice-credential-preflight.test.ts +0 -44
  45. package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +4 -6
  46. package/src/live-voice/__tests__/live-voice-stt.test.ts +1 -1
  47. package/src/live-voice/live-voice-credential-preflight.ts +9 -33
  48. package/src/live-voice/live-voice-session.ts +10 -6
  49. package/src/live-voice/live-voice-tts.ts +1 -8
  50. package/src/plugins/defaults/memory/worker.ts +3 -3
  51. package/src/prompts/persona-resolver.ts +0 -35
  52. package/src/providers/__tests__/search-provider-catalog.test.ts +5 -8
  53. package/src/providers/__tests__/voice-error-copy.test.ts +9 -10
  54. package/src/providers/search-provider-catalog.ts +7 -39
  55. package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +3 -47
  56. package/src/providers/speech-to-text/__tests__/resolve.test.ts +6 -372
  57. package/src/providers/speech-to-text/provider-catalog.ts +2 -73
  58. package/src/providers/speech-to-text/resolve.ts +3 -120
  59. package/src/runtime/http-router-types.ts +1 -3
  60. package/src/runtime/http-server.ts +1 -1
  61. package/src/runtime/routes/__tests__/tts-routes.test.ts +2 -6
  62. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +7 -7
  63. package/src/runtime/routes/index.ts +0 -4
  64. package/src/runtime/routes/live-voice-routes.test.ts +4 -11
  65. package/src/runtime/routes/live-voice-routes.ts +8 -11
  66. package/src/runtime/routes/secret-routes.ts +0 -6
  67. package/src/runtime/routes/tts-routes.ts +0 -38
  68. package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -7
  69. package/src/schedule/__tests__/worker-mcp-readiness.test.ts +0 -4
  70. package/src/schedule/schedule-store.ts +0 -7
  71. package/src/schedule/scheduler.ts +0 -5
  72. package/src/schedule/worker.ts +1 -1
  73. package/src/stt/__tests__/daemon-batch-transcriber.test.ts +2 -10
  74. package/src/stt/daemon-batch-transcriber.ts +4 -38
  75. package/src/stt/types.ts +1 -9
  76. package/src/tools/network/__tests__/web-search.test.ts +3 -250
  77. package/src/tools/network/web-search.ts +4 -165
  78. package/src/tts/__tests__/provider-resolution.test.ts +0 -5
  79. package/src/tts/provider-catalog.ts +0 -4
  80. package/src/tts/types.ts +0 -1
  81. package/src/__tests__/cloud-speech-defaults.test.ts +0 -330
  82. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1708
  83. package/src/__tests__/migration-export-http.test.ts +0 -557
  84. package/src/__tests__/migration-export-streaming.test.ts +0 -350
  85. package/src/__tests__/migration-export-to-gcs.test.ts +0 -706
  86. package/src/__tests__/migration-import-commit-http.test.ts +0 -1176
  87. package/src/__tests__/migration-import-from-gcs.test.ts +0 -534
  88. package/src/__tests__/migration-import-from-path.test.ts +0 -324
  89. package/src/__tests__/migration-import-from-url.test.ts +0 -588
  90. package/src/__tests__/migration-import-preflight-http.test.ts +0 -739
  91. package/src/__tests__/migration-jobs-status.test.ts +0 -139
  92. package/src/__tests__/migration-parity-persistence.test.ts +0 -1891
  93. package/src/__tests__/migration-transport.test.ts +0 -1089
  94. package/src/__tests__/migration-validate-http.test.ts +0 -634
  95. package/src/__tests__/migration-wizard.test.ts +0 -1395
  96. package/src/__tests__/rebind-secrets-screen.test.ts +0 -876
  97. package/src/__tests__/sanitize-config-for-transfer.test.ts +0 -259
  98. package/src/__tests__/transfer-progress-screen.test.ts +0 -1217
  99. package/src/__tests__/validation-results-screen.test.ts +0 -1130
  100. package/src/__tests__/vbundle-pax-and-symlink.test.ts +0 -147
  101. package/src/background-wake/background-wake-routes.test.ts +0 -906
  102. package/src/background-wake/cloud-client.test.ts +0 -328
  103. package/src/background-wake/cloud-client.ts +0 -196
  104. package/src/background-wake/next-wake.test.ts +0 -335
  105. package/src/background-wake/next-wake.ts +0 -188
  106. package/src/background-wake/publisher.ts +0 -93
  107. package/src/background-wake/wake-intent-hooks.test.ts +0 -234
  108. package/src/cli/commands/__tests__/backup.test.ts +0 -363
  109. package/src/cli/commands/backup.help.ts +0 -180
  110. package/src/cli/commands/backup.ts +0 -398
  111. package/src/cloud/cloud-speech.test.ts +0 -335
  112. package/src/cloud/cloud-speech.ts +0 -300
  113. package/src/config/cloud-speech-defaults.ts +0 -300
  114. package/src/config/sanitize-for-transfer.ts +0 -111
  115. package/src/live-voice/__tests__/live-voice-cloud-speech-fallback.test.ts +0 -250
  116. package/src/providers/speech-to-text/__tests__/cadet-cloud-realtime.test.ts +0 -457
  117. package/src/providers/speech-to-text/__tests__/cadet-cloud.test.ts +0 -271
  118. package/src/providers/speech-to-text/__tests__/cadet-speech-relay-connection.test.ts +0 -126
  119. package/src/providers/speech-to-text/cadet-cloud-flux-realtime.ts +0 -309
  120. package/src/providers/speech-to-text/cadet-cloud-realtime.ts +0 -315
  121. package/src/providers/speech-to-text/cadet-cloud.ts +0 -108
  122. package/src/providers/speech-to-text/cadet-speech-relay-connection.ts +0 -176
  123. package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +0 -254
  124. package/src/runtime/migrations/__tests__/job-registry.test.ts +0 -346
  125. package/src/runtime/migrations/__tests__/origin-mode.test.ts +0 -62
  126. package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +0 -184
  127. package/src/runtime/migrations/__tests__/staged-import-path.test.ts +0 -110
  128. package/src/runtime/migrations/__tests__/v1-test-helpers.ts +0 -112
  129. package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +0 -285
  130. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +0 -126
  131. package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +0 -253
  132. package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +0 -208
  133. package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +0 -485
  134. package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +0 -349
  135. package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +0 -196
  136. package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +0 -551
  137. package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +0 -428
  138. package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +0 -220
  139. package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +0 -1823
  140. package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +0 -598
  141. package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +0 -463
  142. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -538
  143. package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +0 -515
  144. package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +0 -453
  145. package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +0 -319
  146. package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +0 -240
  147. package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +0 -473
  148. package/src/runtime/migrations/gcs-signed-url.ts +0 -190
  149. package/src/runtime/migrations/job-registry.ts +0 -297
  150. package/src/runtime/migrations/migration-transport.ts +0 -612
  151. package/src/runtime/migrations/migration-wizard.ts +0 -747
  152. package/src/runtime/migrations/origin-mode.ts +0 -42
  153. package/src/runtime/migrations/rebind-secrets-screen.ts +0 -412
  154. package/src/runtime/migrations/staged-import-path.ts +0 -116
  155. package/src/runtime/migrations/transfer-progress-screen.ts +0 -323
  156. package/src/runtime/migrations/validation-results-screen.ts +0 -469
  157. package/src/runtime/migrations/vbundle-builder.ts +0 -1382
  158. package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -405
  159. package/src/runtime/migrations/vbundle-import-policy.ts +0 -241
  160. package/src/runtime/migrations/vbundle-importer.ts +0 -826
  161. package/src/runtime/migrations/vbundle-metadata-merge.ts +0 -113
  162. package/src/runtime/migrations/vbundle-streaming-importer.ts +0 -2844
  163. package/src/runtime/migrations/vbundle-streaming-validator.ts +0 -426
  164. package/src/runtime/migrations/vbundle-tar-stream.ts +0 -244
  165. package/src/runtime/migrations/vbundle-validator.ts +0 -724
  166. package/src/runtime/routes/__tests__/migration-cadet-metadata-reconcile.test.ts +0 -227
  167. package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +0 -54
  168. package/src/runtime/routes/__tests__/migration-import-allowed-hosts-env.test.ts +0 -70
  169. package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +0 -257
  170. package/src/runtime/routes/background-wake-routes.ts +0 -387
  171. package/src/runtime/routes/migration-routes.ts +0 -2584
  172. package/src/tools/network/__tests__/cloud-search-proxy.test.ts +0 -282
  173. package/src/tools/network/cloud-search-proxy.ts +0 -211
  174. package/src/tts/__tests__/cadet-provider.test.ts +0 -253
  175. package/src/tts/providers/cadet-provider.ts +0 -244
  176. package/src/types/tar-stream.d.ts +0 -66
@@ -1,349 +0,0 @@
1
- /**
2
- * Unit tests for the pure policy module shared by both vbundle importers.
3
- *
4
- * No `node:fs`, no temp dirs — every test exercises a constant or a
5
- * predicate over strings.
6
- */
7
-
8
- import { describe, expect, test } from "bun:test";
9
-
10
- import {
11
- compareSemver,
12
- CONFIG_ARCHIVE_PATHS,
13
- CREDENTIAL_METADATA_ARCHIVE_PATH,
14
- evaluateRuntimeCompatibility,
15
- isConfigArchivePath,
16
- isCredentialMetadataArchivePath,
17
- isLegacyPersonaArchivePath,
18
- isLocalDevRuntimeVersion,
19
- isWorkspaceNamespacedArchivePath,
20
- LEGACY_USER_MD_ARCHIVE_PATH,
21
- partitionWorkspacePreserveSkipDirs,
22
- WORKSPACE_PRESERVE_PATHS,
23
- } from "../vbundle-import-policy.js";
24
-
25
- describe("LEGACY_USER_MD_ARCHIVE_PATH", () => {
26
- test("equals the legacy guardian persona archive path", () => {
27
- expect(LEGACY_USER_MD_ARCHIVE_PATH).toBe("prompts/USER.md");
28
- });
29
- });
30
-
31
- describe("CONFIG_ARCHIVE_PATHS", () => {
32
- test("contains exactly the two known config archive paths", () => {
33
- expect(CONFIG_ARCHIVE_PATHS.size).toBe(2);
34
- expect(CONFIG_ARCHIVE_PATHS.has("workspace/config.json")).toBe(true);
35
- expect(CONFIG_ARCHIVE_PATHS.has("config/settings.json")).toBe(true);
36
- });
37
- });
38
-
39
- describe("CREDENTIAL_METADATA_ARCHIVE_PATH", () => {
40
- test("equals the workspace-namespaced credential metadata path", () => {
41
- expect(CREDENTIAL_METADATA_ARCHIVE_PATH).toBe(
42
- "workspace/data/credentials/metadata.json",
43
- );
44
- });
45
- });
46
-
47
- describe("WORKSPACE_PRESERVE_PATHS", () => {
48
- test("matches the literal 4-element ordered list", () => {
49
- expect(WORKSPACE_PRESERVE_PATHS).toEqual([
50
- "embedding-models",
51
- "deprecated",
52
- "data/db",
53
- "data/qdrant",
54
- ]);
55
- });
56
- });
57
-
58
- describe("isWorkspaceNamespacedArchivePath", () => {
59
- test("true for paths under workspace/", () => {
60
- expect(isWorkspaceNamespacedArchivePath("workspace/foo")).toBe(true);
61
- expect(isWorkspaceNamespacedArchivePath("workspace/data/db/x")).toBe(true);
62
- });
63
-
64
- test("false for non-workspace paths", () => {
65
- expect(isWorkspaceNamespacedArchivePath("prompts/USER.md")).toBe(false);
66
- expect(isWorkspaceNamespacedArchivePath("data/db/assistant.db")).toBe(
67
- false,
68
- );
69
- expect(isWorkspaceNamespacedArchivePath("")).toBe(false);
70
- expect(isWorkspaceNamespacedArchivePath("workspace")).toBe(false);
71
- });
72
- });
73
-
74
- describe("isLegacyPersonaArchivePath", () => {
75
- test("true only for the exact legacy path", () => {
76
- expect(isLegacyPersonaArchivePath("prompts/USER.md")).toBe(true);
77
- });
78
-
79
- test("false for near-misses and unrelated paths", () => {
80
- expect(isLegacyPersonaArchivePath("prompts/USER")).toBe(false);
81
- expect(isLegacyPersonaArchivePath("workspace/prompts/USER.md")).toBe(false);
82
- expect(isLegacyPersonaArchivePath("")).toBe(false);
83
- });
84
- });
85
-
86
- describe("isConfigArchivePath", () => {
87
- test("true for both members of CONFIG_ARCHIVE_PATHS", () => {
88
- expect(isConfigArchivePath("workspace/config.json")).toBe(true);
89
- expect(isConfigArchivePath("config/settings.json")).toBe(true);
90
- });
91
-
92
- test("false for non-members", () => {
93
- expect(isConfigArchivePath("workspace/foo.json")).toBe(false);
94
- expect(isConfigArchivePath("config/settings")).toBe(false);
95
- expect(isConfigArchivePath("")).toBe(false);
96
- });
97
- });
98
-
99
- describe("isCredentialMetadataArchivePath", () => {
100
- test("true for the exact constant", () => {
101
- expect(
102
- isCredentialMetadataArchivePath(
103
- "workspace/data/credentials/metadata.json",
104
- ),
105
- ).toBe(true);
106
- });
107
-
108
- test("false for the legacy non-prefixed form and empty string", () => {
109
- expect(
110
- isCredentialMetadataArchivePath("data/credentials/metadata.json"),
111
- ).toBe(false);
112
- expect(isCredentialMetadataArchivePath("")).toBe(false);
113
- });
114
- });
115
-
116
- describe("partitionWorkspacePreserveSkipDirs", () => {
117
- test("splits preserve-paths into top-level vs data-subdir skip sets", () => {
118
- const { topLevelSkipDirs, dataSubdirSkipDirs } =
119
- partitionWorkspacePreserveSkipDirs();
120
-
121
- expect(topLevelSkipDirs.size).toBe(2);
122
- expect(topLevelSkipDirs.has("embedding-models")).toBe(true);
123
- expect(topLevelSkipDirs.has("deprecated")).toBe(true);
124
-
125
- expect(dataSubdirSkipDirs.size).toBe(2);
126
- expect(dataSubdirSkipDirs.has("db")).toBe(true);
127
- expect(dataSubdirSkipDirs.has("qdrant")).toBe(true);
128
- });
129
- });
130
-
131
- describe("compareSemver", () => {
132
- test("0.10.0 > 0.9.0 (numeric, not lexical)", () => {
133
- expect(compareSemver("0.10.0", "0.9.0")).toBe(1);
134
- });
135
-
136
- test("equal triples return 0", () => {
137
- expect(compareSemver("0.7.1", "0.7.1")).toBe(0);
138
- });
139
-
140
- test("smaller patch returns -1", () => {
141
- expect(compareSemver("0.7.0", "0.7.1")).toBe(-1);
142
- });
143
-
144
- test("prerelease tag is stripped (treated as base release)", () => {
145
- expect(compareSemver("0.7.1-staging.1", "0.7.1")).toBe(0);
146
- });
147
-
148
- test("non-version string returns null", () => {
149
- expect(compareSemver("not-a-version", "0.7.1")).toBe(null);
150
- });
151
-
152
- test("two-part version returns null", () => {
153
- expect(compareSemver("1.2", "0.7.1")).toBe(null);
154
- });
155
-
156
- // Regression: Number.parseInt accepts numeric prefixes and ignores
157
- // trailing junk ("0foo" → 0), which previously coerced malformed
158
- // triples through the gate. parseSemverTriple now requires each
159
- // component to match /^\d+$/ exactly.
160
- test("trailing junk on a component returns null (left arg)", () => {
161
- expect(compareSemver("0.8.0foo", "0.8.0")).toBe(null);
162
- });
163
-
164
- test("trailing junk on a component returns null (right arg)", () => {
165
- expect(compareSemver("0.8.0", "0.7.1xyz")).toBe(null);
166
- });
167
-
168
- // Leading zeros are accepted: "01.02.03" parses to the same numeric
169
- // triple as "1.2.3" since Number("01") === 1. We pin this behavior
170
- // so a future tightening doesn't accidentally regress callers that
171
- // pass zero-padded versions from upstream tooling.
172
- test("leading zeros parse equal to un-padded triple", () => {
173
- expect(compareSemver("01.02.03", "1.2.3")).toBe(0);
174
- });
175
-
176
- test("leading whitespace returns null", () => {
177
- expect(compareSemver(" 0.8.0", "0.8.0")).toBe(null);
178
- });
179
-
180
- test("negative component returns null", () => {
181
- expect(compareSemver("0.8.-1", "0.8.0")).toBe(null);
182
- });
183
- });
184
-
185
- describe("evaluateRuntimeCompatibility", () => {
186
- test("legacy sentinel passes regardless of runtime version", () => {
187
- expect(
188
- evaluateRuntimeCompatibility(
189
- { min_runtime_version: "0.0.0-legacy", max_runtime_version: null },
190
- "0.7.1",
191
- ),
192
- ).toEqual({ ok: true });
193
- });
194
-
195
- test("runtime above min with no max passes", () => {
196
- expect(
197
- evaluateRuntimeCompatibility(
198
- { min_runtime_version: "0.7.0", max_runtime_version: null },
199
- "0.7.1",
200
- ),
201
- ).toEqual({ ok: true });
202
- });
203
-
204
- test("runtime below min fails with full echo", () => {
205
- const compat = {
206
- min_runtime_version: "0.8.0",
207
- max_runtime_version: null,
208
- };
209
- expect(evaluateRuntimeCompatibility(compat, "0.7.1")).toEqual({
210
- ok: false,
211
- reason: "version_incompatible",
212
- bundle_compat: compat,
213
- runtime_version: "0.7.1",
214
- });
215
- });
216
-
217
- test("runtime above max fails", () => {
218
- const compat = {
219
- min_runtime_version: "0.7.0",
220
- max_runtime_version: "0.7.5",
221
- };
222
- expect(evaluateRuntimeCompatibility(compat, "0.8.0")).toEqual({
223
- ok: false,
224
- reason: "version_incompatible",
225
- bundle_compat: compat,
226
- runtime_version: "0.8.0",
227
- });
228
- });
229
-
230
- test("max is inclusive", () => {
231
- expect(
232
- evaluateRuntimeCompatibility(
233
- { min_runtime_version: "0.7.0", max_runtime_version: "0.7.5" },
234
- "0.7.5",
235
- ),
236
- ).toEqual({ ok: true });
237
- });
238
-
239
- test("unparsable min skips the gate (fail-open)", () => {
240
- expect(
241
- evaluateRuntimeCompatibility(
242
- { min_runtime_version: "garbage", max_runtime_version: null },
243
- "0.7.1",
244
- ),
245
- ).toEqual({ ok: true });
246
- });
247
-
248
- // Regression for Codex feedback: a malformed min like "0.8.0foo"
249
- // previously coerced to [0, 8, 0] via Number.parseInt and incorrectly
250
- // produced a version_incompatible decision against runtime "0.7.1".
251
- // With strict per-component digit matching, the parse fails and the
252
- // gate fails open.
253
- test("malformed min with trailing junk fails open, does not block", () => {
254
- expect(
255
- evaluateRuntimeCompatibility(
256
- { min_runtime_version: "0.8.0foo", max_runtime_version: null },
257
- "0.7.1",
258
- ),
259
- ).toEqual({ ok: true });
260
- });
261
-
262
- // A `vel up` minikube assistant image is stamped
263
- // `<pkg.version>-local.<ts>.<sha>` from the checkout's package.json,
264
- // which only moves on release cuts, so the minimum bound cannot be
265
- // decided from it; the explicit maximum ceiling still applies.
266
- test("local dev runtime build skips the minimum bound", () => {
267
- expect(
268
- evaluateRuntimeCompatibility(
269
- { min_runtime_version: "0.12.2-staging.4", max_runtime_version: null },
270
- "0.12.1-local.20260916144827.2f409056e6",
271
- ),
272
- ).toEqual({ ok: true });
273
- });
274
-
275
- test("local dev runtime build within an explicit range passes", () => {
276
- expect(
277
- evaluateRuntimeCompatibility(
278
- { min_runtime_version: "0.12.2", max_runtime_version: "0.12.5" },
279
- "0.12.1-local.20260916144827.2f409056e6",
280
- ),
281
- ).toEqual({ ok: true });
282
- });
283
-
284
- test("local dev runtime build above max still fails", () => {
285
- const compat = {
286
- min_runtime_version: "0.7.0",
287
- max_runtime_version: "0.7.5",
288
- };
289
- const runtime = "0.12.1-local.20260916144827.2f409056e6";
290
- expect(evaluateRuntimeCompatibility(compat, runtime)).toEqual({
291
- ok: false,
292
- reason: "version_incompatible",
293
- bundle_compat: compat,
294
- runtime_version: runtime,
295
- });
296
- });
297
-
298
- test("local dev runtime build with unparsable max fails open", () => {
299
- expect(
300
- evaluateRuntimeCompatibility(
301
- { min_runtime_version: "0.7.0", max_runtime_version: "garbage" },
302
- "0.12.1-local.20260916144827.2f409056e6",
303
- ),
304
- ).toEqual({ ok: true });
305
- });
306
-
307
- test("other prerelease builds are still gated", () => {
308
- const compat = {
309
- min_runtime_version: "0.12.2-staging.4",
310
- max_runtime_version: null,
311
- };
312
- for (const runtime of [
313
- "0.12.1-dev.202609151913.a151da3",
314
- "0.12.1-staging.9",
315
- "0.12.1",
316
- ]) {
317
- expect(evaluateRuntimeCompatibility(compat, runtime)).toEqual({
318
- ok: false,
319
- reason: "version_incompatible",
320
- bundle_compat: compat,
321
- runtime_version: runtime,
322
- });
323
- }
324
- });
325
- });
326
-
327
- describe("isLocalDevRuntimeVersion", () => {
328
- test("matches the local hatch / minikube version stamp", () => {
329
- expect(
330
- isLocalDevRuntimeVersion("0.12.1-local.20260916144827.2f409056e6"),
331
- ).toBe(true);
332
- expect(isLocalDevRuntimeVersion("0.0.0-local.1789584535000")).toBe(true);
333
- });
334
-
335
- test("rejects release, dev and staging builds and lookalikes", () => {
336
- for (const version of [
337
- "0.12.1",
338
- "0.12.1-dev.202609151913.a151da3",
339
- "0.12.2-staging.4",
340
- "0.0.0-dev",
341
- "0.0.0-legacy",
342
- "0.12.1-localhost.1",
343
- "local.0.12.1",
344
- "",
345
- ]) {
346
- expect(isLocalDevRuntimeVersion(version)).toBe(false);
347
- }
348
- });
349
- });
@@ -1,196 +0,0 @@
1
- /**
2
- * Buffer importer runtime-version compat gate.
3
- *
4
- * Wires `commitImport` to `policy.evaluateRuntimeCompatibility` BEFORE any
5
- * state mutation. The cloud-side gate is the primary check; this catches
6
- * legacy bundles whose ExportJob row predates PR #5470 (compat columns NULL
7
- * → platform gate skipped) and any caller that bypasses the cloud-issued
8
- * signed URL flow.
9
- *
10
- * Invariants pinned here:
11
- * - A compatible bundle imports normally.
12
- * - An incompatible bundle (min_runtime_version above APP_VERSION) returns
13
- * `{ ok: false, reason: "version_incompatible", bundle_compat,
14
- * runtime_version }` with NO disk mutation.
15
- * - The legacy sentinel "0.0.0-legacy" passes the gate unconditionally.
16
- * - Pre-existing workspace files are byte-identical and no `*.backup-*`
17
- * files are created when the gate rejects.
18
- */
19
-
20
- import {
21
- existsSync,
22
- mkdirSync,
23
- mkdtempSync,
24
- readdirSync,
25
- readFileSync,
26
- realpathSync,
27
- rmSync,
28
- writeFileSync,
29
- } from "node:fs";
30
- import { tmpdir } from "node:os";
31
- import { join, relative } from "node:path";
32
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
33
-
34
- import { APP_VERSION } from "../../../version.js";
35
- import { buildVBundle } from "../vbundle-builder.js";
36
- import { DefaultPathResolver } from "../vbundle-import-analyzer.js";
37
- import { commitImport } from "../vbundle-importer.js";
38
- import { defaultV1Options } from "./v1-test-helpers.js";
39
-
40
- // ---------------------------------------------------------------------------
41
- // Helpers
42
- // ---------------------------------------------------------------------------
43
-
44
- function freshWorkspace(): string {
45
- const parent = realpathSync(
46
- mkdtempSync(join(tmpdir(), "vbundle-import-version-compat-")),
47
- );
48
- return join(parent, "workspace");
49
- }
50
-
51
- function cleanupWorkspaceParent(workspaceDir: string): void {
52
- try {
53
- rmSync(join(workspaceDir, ".."), { recursive: true, force: true });
54
- } catch {
55
- // best-effort
56
- }
57
- }
58
-
59
- /** Recursively yields every regular file under `root` as a relative path. */
60
- function* walkFiles(root: string): Generator<string> {
61
- if (!existsSync(root)) {
62
- return;
63
- }
64
- const stack: string[] = [root];
65
- while (stack.length > 0) {
66
- const dir = stack.pop()!;
67
- let entries;
68
- try {
69
- entries = readdirSync(dir, { withFileTypes: true });
70
- } catch {
71
- continue;
72
- }
73
- for (const entry of entries) {
74
- const abs = join(dir, entry.name);
75
- if (entry.isDirectory()) {
76
- stack.push(abs);
77
- } else if (entry.isFile()) {
78
- yield relative(root, abs);
79
- }
80
- }
81
- }
82
- }
83
-
84
- function buildBundleWithMinRuntimeVersion(
85
- minRuntimeVersion: string,
86
- ): Uint8Array {
87
- const dbBytes = new TextEncoder().encode("db-payload");
88
- const configJson = JSON.stringify({ version: 1 });
89
- const { archive } = buildVBundle({
90
- files: [
91
- { path: "workspace/data/db/assistant.db", data: dbBytes },
92
- {
93
- path: "workspace/config.json",
94
- data: new TextEncoder().encode(configJson),
95
- },
96
- ],
97
- ...defaultV1Options(),
98
- compatibility: {
99
- min_runtime_version: minRuntimeVersion,
100
- max_runtime_version: null,
101
- },
102
- });
103
- return archive;
104
- }
105
-
106
- // ---------------------------------------------------------------------------
107
- // Tests
108
- // ---------------------------------------------------------------------------
109
-
110
- describe("commitImport runtime-version compat gate", () => {
111
- let workspaceDir: string;
112
-
113
- beforeEach(() => {
114
- workspaceDir = freshWorkspace();
115
- mkdirSync(workspaceDir, { recursive: true });
116
- });
117
-
118
- afterEach(() => {
119
- cleanupWorkspaceParent(workspaceDir);
120
- });
121
-
122
- test("compatible bundle imports normally", () => {
123
- const archive = buildBundleWithMinRuntimeVersion("0.0.1");
124
-
125
- const result = commitImport({
126
- archiveData: archive,
127
- pathResolver: new DefaultPathResolver(workspaceDir),
128
- workspaceDir,
129
- });
130
-
131
- expect(result.ok).toBe(true);
132
- expect(existsSync(join(workspaceDir, "data/db/assistant.db"))).toBe(true);
133
- expect(existsSync(join(workspaceDir, "config.json"))).toBe(true);
134
- });
135
-
136
- test("incompatible bundle (above APP_VERSION) returns version_incompatible without writing", () => {
137
- const archive = buildBundleWithMinRuntimeVersion("99.0.0");
138
-
139
- const result = commitImport({
140
- archiveData: archive,
141
- pathResolver: new DefaultPathResolver(workspaceDir),
142
- workspaceDir,
143
- });
144
-
145
- expect(result.ok).toBe(false);
146
- if (!result.ok && result.reason === "version_incompatible") {
147
- expect(result.bundle_compat.min_runtime_version).toBe("99.0.0");
148
- expect(result.bundle_compat.max_runtime_version).toBeNull();
149
- expect(result.runtime_version).toBe(APP_VERSION);
150
- } else {
151
- throw new Error(
152
- `expected version_incompatible, got ${JSON.stringify(result)}`,
153
- );
154
- }
155
-
156
- // Fresh workspace stays empty — no bundle files, no backups.
157
- expect([...walkFiles(workspaceDir)]).toEqual([]);
158
- });
159
-
160
- test("legacy sentinel passes through", () => {
161
- const archive = buildBundleWithMinRuntimeVersion("0.0.0-legacy");
162
-
163
- const result = commitImport({
164
- archiveData: archive,
165
- pathResolver: new DefaultPathResolver(workspaceDir),
166
- workspaceDir,
167
- });
168
-
169
- expect(result.ok).toBe(true);
170
- expect(existsSync(join(workspaceDir, "data/db/assistant.db"))).toBe(true);
171
- expect(existsSync(join(workspaceDir, "config.json"))).toBe(true);
172
- });
173
-
174
- test("pre-existing workspace files stay untouched on incompatibility", () => {
175
- const sentinelPath = join(workspaceDir, "sentinel.txt");
176
- const sentinelContent = "keep me";
177
- writeFileSync(sentinelPath, sentinelContent);
178
-
179
- const archive = buildBundleWithMinRuntimeVersion("99.0.0");
180
-
181
- const result = commitImport({
182
- archiveData: archive,
183
- pathResolver: new DefaultPathResolver(workspaceDir),
184
- workspaceDir,
185
- });
186
-
187
- expect(result.ok).toBe(false);
188
- if (!result.ok) {
189
- expect(result.reason).toBe("version_incompatible");
190
- }
191
-
192
- expect(readFileSync(sentinelPath, "utf-8")).toBe(sentinelContent);
193
- // Only the seeded sentinel remains — no bundle files, no `*.backup-*`.
194
- expect([...walkFiles(workspaceDir)]).toEqual(["sentinel.txt"]);
195
- });
196
- });