@vellumai/cli 0.11.6 → 0.11.7-staging.2

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 (807) hide show
  1. package/AGENTS.md +3 -1
  2. package/knip.json +1 -0
  3. package/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  4. package/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  5. package/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  6. package/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  7. package/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  8. package/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  9. package/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  10. package/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  11. package/node_modules/@vellumai/environments/package.json +2 -1
  12. package/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  13. package/node_modules/@vellumai/environments/src/shell.ts +65 -0
  14. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/package.json +18 -0
  15. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/__tests__/manifest.test.ts +173 -0
  16. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/__tests__/read.test.ts +114 -0
  17. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/index.ts +29 -0
  18. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/layout.ts +18 -0
  19. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/manifest.ts +156 -0
  20. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/src/read.ts +109 -0
  21. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/avatar-manifest/tsconfig.json +20 -0
  22. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/package.json +2 -1
  23. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/shell.test.ts +56 -0
  24. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/environments/src/shell.ts +65 -0
  25. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/LICENSE +21 -0
  26. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/README.md +208 -0
  27. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/index.cjs +33 -0
  28. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/index.d.cts +4 -0
  29. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/index.d.ts +4 -0
  30. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/index.js +4 -0
  31. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/locales/index.cjs +17 -0
  32. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/locales/index.d.cts +1 -0
  33. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/locales/index.d.ts +1 -0
  34. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/locales/index.js +1 -0
  35. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/locales/package.json +6 -0
  36. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/mini/index.cjs +32 -0
  37. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/mini/index.d.cts +3 -0
  38. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/mini/index.d.ts +3 -0
  39. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/mini/index.js +3 -0
  40. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/mini/package.json +6 -0
  41. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/package.json +135 -0
  42. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/index.ts +4 -0
  43. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/locales/index.ts +1 -0
  44. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/mini/index.ts +3 -0
  45. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/ZodError.ts +330 -0
  46. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/datetime.ts +58 -0
  47. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/discriminatedUnion.ts +80 -0
  48. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/index.ts +59 -0
  49. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/ipv4.ts +57 -0
  50. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/object.ts +69 -0
  51. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/primitives.ts +162 -0
  52. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/realworld.ts +63 -0
  53. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/string.ts +55 -0
  54. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/benchmarks/union.ts +80 -0
  55. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/errors.ts +13 -0
  56. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/external.ts +6 -0
  57. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/enumUtil.ts +17 -0
  58. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/errorUtil.ts +8 -0
  59. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/parseUtil.ts +176 -0
  60. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/partialUtil.ts +34 -0
  61. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/typeAliases.ts +2 -0
  62. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/helpers/util.ts +224 -0
  63. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/index.ts +4 -0
  64. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/locales/en.ts +124 -0
  65. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/standard-schema.ts +113 -0
  66. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/Mocker.ts +54 -0
  67. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/all-errors.test.ts +157 -0
  68. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/anyunknown.test.ts +28 -0
  69. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/array.test.ts +71 -0
  70. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/async-parsing.test.ts +388 -0
  71. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/async-refinements.test.ts +46 -0
  72. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/base.test.ts +29 -0
  73. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/bigint.test.ts +55 -0
  74. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/branded.test.ts +53 -0
  75. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/catch.test.ts +220 -0
  76. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/coerce.test.ts +133 -0
  77. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/complex.test.ts +70 -0
  78. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/custom.test.ts +31 -0
  79. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/date.test.ts +32 -0
  80. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/deepmasking.test.ts +186 -0
  81. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/default.test.ts +112 -0
  82. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/description.test.ts +33 -0
  83. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/discriminated-unions.test.ts +315 -0
  84. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/enum.test.ts +80 -0
  85. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/error.test.ts +551 -0
  86. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/firstparty.test.ts +87 -0
  87. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/firstpartyschematypes.test.ts +21 -0
  88. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/function.test.ts +261 -0
  89. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/generics.test.ts +48 -0
  90. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/instanceof.test.ts +37 -0
  91. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/intersection.test.ts +110 -0
  92. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/language-server.source.ts +76 -0
  93. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/language-server.test.ts +207 -0
  94. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/literal.test.ts +36 -0
  95. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/map.test.ts +110 -0
  96. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/masking.test.ts +4 -0
  97. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/mocker.test.ts +19 -0
  98. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/nan.test.ts +24 -0
  99. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/nativeEnum.test.ts +87 -0
  100. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/nullable.test.ts +42 -0
  101. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/number.test.ts +176 -0
  102. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/object-augmentation.test.ts +29 -0
  103. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/object-in-es5-env.test.ts +29 -0
  104. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/object.test.ts +434 -0
  105. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/optional.test.ts +42 -0
  106. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/parseUtil.test.ts +23 -0
  107. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/parser.test.ts +41 -0
  108. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/partials.test.ts +243 -0
  109. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/pickomit.test.ts +111 -0
  110. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/pipeline.test.ts +29 -0
  111. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/preprocess.test.ts +186 -0
  112. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/primitive.test.ts +440 -0
  113. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/promise.test.ts +90 -0
  114. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/readonly.test.ts +194 -0
  115. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/record.test.ts +171 -0
  116. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/recursive.test.ts +197 -0
  117. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/refine.test.ts +313 -0
  118. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/safeparse.test.ts +27 -0
  119. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/set.test.ts +142 -0
  120. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/standard-schema.test.ts +83 -0
  121. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/string.test.ts +916 -0
  122. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/transformer.test.ts +233 -0
  123. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/tuple.test.ts +90 -0
  124. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/unions.test.ts +57 -0
  125. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/validations.test.ts +133 -0
  126. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/tests/void.test.ts +15 -0
  127. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v3/types.ts +5138 -0
  128. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/checks.ts +32 -0
  129. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/coerce.ts +27 -0
  130. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/compat.ts +70 -0
  131. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/errors.ts +82 -0
  132. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/external.ts +51 -0
  133. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/from-json-schema.ts +643 -0
  134. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/index.ts +5 -0
  135. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/iso.ts +90 -0
  136. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/parse.ts +82 -0
  137. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/schemas.ts +2409 -0
  138. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/anyunknown.test.ts +26 -0
  139. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/apply.test.ts +59 -0
  140. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/array.test.ts +264 -0
  141. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/assignability.test.ts +210 -0
  142. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/async-parsing.test.ts +381 -0
  143. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/async-refinements.test.ts +68 -0
  144. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/base.test.ts +7 -0
  145. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/bigint.test.ts +54 -0
  146. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/brand.test.ts +106 -0
  147. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/catch.test.ts +276 -0
  148. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/coalesce.test.ts +20 -0
  149. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/codec-examples.test.ts +573 -0
  150. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/codec.test.ts +562 -0
  151. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/coerce.test.ts +160 -0
  152. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/continuability.test.ts +374 -0
  153. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/custom.test.ts +40 -0
  154. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/date.test.ts +62 -0
  155. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/datetime.test.ts +302 -0
  156. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/default.test.ts +365 -0
  157. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
  158. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/description.test.ts +32 -0
  159. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +661 -0
  160. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/enum.test.ts +285 -0
  161. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/error-utils.test.ts +595 -0
  162. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/error.test.ts +711 -0
  163. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/file.test.ts +96 -0
  164. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/firstparty.test.ts +179 -0
  165. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/fix-json-issue.test.ts +26 -0
  166. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +734 -0
  167. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/function.test.ts +360 -0
  168. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/generics.test.ts +72 -0
  169. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/hash.test.ts +68 -0
  170. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/index.test.ts +939 -0
  171. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/instanceof.test.ts +60 -0
  172. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/intersection.test.ts +198 -0
  173. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/json.test.ts +109 -0
  174. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/lazy.test.ts +227 -0
  175. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/literal.test.ts +117 -0
  176. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/map.test.ts +330 -0
  177. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/nan.test.ts +21 -0
  178. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/nested-refine.test.ts +168 -0
  179. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/nonoptional.test.ts +101 -0
  180. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/nullable.test.ts +22 -0
  181. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/number.test.ts +270 -0
  182. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/object.test.ts +640 -0
  183. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/optional.test.ts +223 -0
  184. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/partial.test.ts +427 -0
  185. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/pickomit.test.ts +211 -0
  186. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/pipe.test.ts +101 -0
  187. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/prefault.test.ts +74 -0
  188. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/preprocess.test.ts +282 -0
  189. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/primitive.test.ts +175 -0
  190. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/promise.test.ts +81 -0
  191. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/prototypes.test.ts +23 -0
  192. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/readonly.test.ts +252 -0
  193. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/record.test.ts +632 -0
  194. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +582 -0
  195. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/refine.test.ts +570 -0
  196. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/registries.test.ts +243 -0
  197. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/set.test.ts +181 -0
  198. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/standard-schema.test.ts +134 -0
  199. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/string-formats.test.ts +125 -0
  200. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/string.test.ts +1175 -0
  201. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/stringbool.test.ts +106 -0
  202. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +771 -0
  203. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/to-json-schema-methods.test.ts +438 -0
  204. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +2990 -0
  205. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/transform.test.ts +361 -0
  206. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/tuple.test.ts +183 -0
  207. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/union.test.ts +219 -0
  208. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/url.test.ts +13 -0
  209. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/validations.test.ts +283 -0
  210. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/classic/tests/void.test.ts +12 -0
  211. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/api.ts +1798 -0
  212. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/checks.ts +1293 -0
  213. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/config.ts +15 -0
  214. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/core.ts +138 -0
  215. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/doc.ts +44 -0
  216. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/errors.ts +448 -0
  217. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/index.ts +16 -0
  218. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/json-schema-generator.ts +126 -0
  219. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/json-schema-processors.ts +667 -0
  220. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/json-schema.ts +147 -0
  221. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/parse.ts +195 -0
  222. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/regexes.ts +183 -0
  223. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/registries.ts +105 -0
  224. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/schemas.ts +4538 -0
  225. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/standard-schema.ts +159 -0
  226. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/extend.test.ts +59 -0
  227. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/index.test.ts +46 -0
  228. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/be.test.ts +124 -0
  229. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/en.test.ts +22 -0
  230. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/es.test.ts +181 -0
  231. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/he.test.ts +379 -0
  232. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/nl.test.ts +46 -0
  233. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/ru.test.ts +128 -0
  234. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/tr.test.ts +69 -0
  235. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/locales/uz.test.ts +83 -0
  236. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/record-constructor.test.ts +67 -0
  237. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/tests/recursive-tuples.test.ts +45 -0
  238. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/to-json-schema.ts +613 -0
  239. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/util.ts +966 -0
  240. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/versions.ts +5 -0
  241. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/core/zsf.ts +323 -0
  242. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/index.ts +4 -0
  243. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ar.ts +115 -0
  244. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/az.ts +111 -0
  245. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/be.ts +176 -0
  246. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/bg.ts +128 -0
  247. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ca.ts +116 -0
  248. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/cs.ts +118 -0
  249. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/da.ts +123 -0
  250. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/de.ts +116 -0
  251. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/en.ts +119 -0
  252. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/eo.ts +118 -0
  253. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/es.ts +141 -0
  254. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/fa.ts +126 -0
  255. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/fi.ts +121 -0
  256. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/fr-CA.ts +116 -0
  257. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/fr.ts +116 -0
  258. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/he.ts +246 -0
  259. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/hu.ts +117 -0
  260. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/hy.ts +164 -0
  261. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/id.ts +115 -0
  262. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/index.ts +49 -0
  263. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/is.ts +119 -0
  264. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/it.ts +116 -0
  265. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ja.ts +114 -0
  266. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ka.ts +123 -0
  267. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/kh.ts +7 -0
  268. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/km.ts +119 -0
  269. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ko.ts +121 -0
  270. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/lt.ts +239 -0
  271. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/mk.ts +118 -0
  272. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ms.ts +115 -0
  273. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/nl.ts +121 -0
  274. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/no.ts +116 -0
  275. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ota.ts +117 -0
  276. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/pl.ts +118 -0
  277. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ps.ts +126 -0
  278. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/pt.ts +116 -0
  279. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ru.ts +176 -0
  280. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/sl.ts +118 -0
  281. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/sv.ts +119 -0
  282. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ta.ts +118 -0
  283. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/th.ts +119 -0
  284. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/tr.ts +111 -0
  285. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ua.ts +7 -0
  286. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/uk.ts +117 -0
  287. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/ur.ts +119 -0
  288. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/uz.ts +116 -0
  289. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/vi.ts +117 -0
  290. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/yo.ts +124 -0
  291. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/zh-CN.ts +116 -0
  292. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/locales/zh-TW.ts +115 -0
  293. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/checks.ts +32 -0
  294. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/coerce.ts +27 -0
  295. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/external.ts +40 -0
  296. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/index.ts +3 -0
  297. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/iso.ts +66 -0
  298. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/parse.ts +14 -0
  299. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/schemas.ts +1916 -0
  300. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/apply.test.ts +24 -0
  301. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/assignability.test.ts +129 -0
  302. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/brand.test.ts +94 -0
  303. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/checks.test.ts +144 -0
  304. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/codec.test.ts +529 -0
  305. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/computed.test.ts +36 -0
  306. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/error.test.ts +22 -0
  307. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/functions.test.ts +5 -0
  308. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/index.test.ts +963 -0
  309. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/number.test.ts +95 -0
  310. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/object.test.ts +227 -0
  311. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/prototypes.test.ts +43 -0
  312. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +275 -0
  313. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/standard-schema.test.ts +50 -0
  314. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4/mini/tests/string.test.ts +347 -0
  315. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/src/v4-mini/index.ts +3 -0
  316. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/ZodError.cjs +138 -0
  317. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/ZodError.d.cts +164 -0
  318. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/ZodError.d.ts +164 -0
  319. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/ZodError.js +133 -0
  320. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/errors.cjs +17 -0
  321. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/errors.d.cts +5 -0
  322. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/errors.d.ts +5 -0
  323. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/errors.js +9 -0
  324. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/external.cjs +22 -0
  325. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/external.d.cts +6 -0
  326. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/external.d.ts +6 -0
  327. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/external.js +6 -0
  328. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/enumUtil.cjs +2 -0
  329. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/enumUtil.d.cts +8 -0
  330. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/enumUtil.d.ts +8 -0
  331. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/enumUtil.js +1 -0
  332. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/errorUtil.cjs +9 -0
  333. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/errorUtil.d.cts +9 -0
  334. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/errorUtil.d.ts +9 -0
  335. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/errorUtil.js +6 -0
  336. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/parseUtil.cjs +124 -0
  337. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/parseUtil.d.cts +78 -0
  338. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/parseUtil.d.ts +78 -0
  339. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/parseUtil.js +109 -0
  340. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/partialUtil.cjs +2 -0
  341. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/partialUtil.d.cts +8 -0
  342. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/partialUtil.d.ts +8 -0
  343. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/partialUtil.js +1 -0
  344. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/typeAliases.cjs +2 -0
  345. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/typeAliases.d.cts +2 -0
  346. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/typeAliases.d.ts +2 -0
  347. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/typeAliases.js +1 -0
  348. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/util.cjs +137 -0
  349. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/util.d.cts +85 -0
  350. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/util.d.ts +85 -0
  351. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/helpers/util.js +133 -0
  352. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/index.cjs +33 -0
  353. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/index.d.cts +4 -0
  354. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/index.d.ts +4 -0
  355. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/index.js +4 -0
  356. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/locales/en.cjs +112 -0
  357. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/locales/en.d.cts +3 -0
  358. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/locales/en.d.ts +3 -0
  359. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/locales/en.js +109 -0
  360. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/package.json +6 -0
  361. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/standard-schema.cjs +2 -0
  362. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/standard-schema.d.cts +102 -0
  363. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/standard-schema.d.ts +102 -0
  364. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/standard-schema.js +1 -0
  365. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/types.cjs +3777 -0
  366. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/types.d.cts +1034 -0
  367. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/types.d.ts +1034 -0
  368. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v3/types.js +3695 -0
  369. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/checks.cjs +33 -0
  370. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/checks.d.cts +1 -0
  371. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/checks.d.ts +1 -0
  372. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/checks.js +1 -0
  373. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/coerce.cjs +47 -0
  374. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/coerce.d.cts +17 -0
  375. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/coerce.d.ts +17 -0
  376. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/coerce.js +17 -0
  377. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/compat.cjs +61 -0
  378. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/compat.d.cts +50 -0
  379. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/compat.d.ts +50 -0
  380. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/compat.js +31 -0
  381. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/errors.cjs +74 -0
  382. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/errors.d.cts +30 -0
  383. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/errors.d.ts +30 -0
  384. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/errors.js +48 -0
  385. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/external.cjs +73 -0
  386. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/external.d.cts +15 -0
  387. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/external.d.ts +15 -0
  388. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/external.js +20 -0
  389. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/from-json-schema.cjs +610 -0
  390. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/from-json-schema.d.cts +12 -0
  391. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/from-json-schema.d.ts +12 -0
  392. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/from-json-schema.js +584 -0
  393. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/index.cjs +33 -0
  394. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/index.d.cts +4 -0
  395. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/index.d.ts +4 -0
  396. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/index.js +4 -0
  397. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/iso.cjs +60 -0
  398. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/iso.d.cts +22 -0
  399. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/iso.d.ts +22 -0
  400. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/iso.js +30 -0
  401. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/package.json +6 -0
  402. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/parse.cjs +41 -0
  403. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/parse.d.cts +31 -0
  404. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/parse.d.ts +31 -0
  405. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/parse.js +15 -0
  406. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/schemas.cjs +1272 -0
  407. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/schemas.d.cts +739 -0
  408. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/schemas.d.ts +739 -0
  409. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/classic/schemas.js +1157 -0
  410. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/api.cjs +1222 -0
  411. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/api.d.cts +304 -0
  412. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/api.d.ts +304 -0
  413. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/api.js +1082 -0
  414. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/checks.cjs +601 -0
  415. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/checks.d.cts +278 -0
  416. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/checks.d.ts +278 -0
  417. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/checks.js +575 -0
  418. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/core.cjs +83 -0
  419. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/core.d.cts +70 -0
  420. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/core.d.ts +70 -0
  421. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/core.js +76 -0
  422. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/doc.cjs +39 -0
  423. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/doc.d.cts +14 -0
  424. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/doc.d.ts +14 -0
  425. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/doc.js +35 -0
  426. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/errors.cjs +213 -0
  427. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/errors.d.cts +220 -0
  428. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/errors.d.ts +220 -0
  429. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/errors.js +182 -0
  430. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/index.cjs +47 -0
  431. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/index.d.cts +16 -0
  432. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/index.d.ts +16 -0
  433. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/index.js +16 -0
  434. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-generator.cjs +99 -0
  435. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-generator.d.cts +65 -0
  436. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-generator.d.ts +65 -0
  437. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-generator.js +95 -0
  438. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-processors.cjs +648 -0
  439. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-processors.d.cts +49 -0
  440. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-processors.d.ts +49 -0
  441. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema-processors.js +605 -0
  442. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema.cjs +2 -0
  443. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema.d.cts +88 -0
  444. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema.d.ts +88 -0
  445. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/json-schema.js +1 -0
  446. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/package.json +6 -0
  447. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/parse.cjs +131 -0
  448. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/parse.d.cts +49 -0
  449. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/parse.d.ts +49 -0
  450. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/parse.js +93 -0
  451. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/regexes.cjs +166 -0
  452. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/regexes.d.cts +79 -0
  453. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/regexes.d.ts +79 -0
  454. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/regexes.js +133 -0
  455. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/registries.cjs +56 -0
  456. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/registries.d.cts +35 -0
  457. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/registries.d.ts +35 -0
  458. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/registries.js +51 -0
  459. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/schemas.cjs +2124 -0
  460. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/schemas.d.cts +1146 -0
  461. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/schemas.d.ts +1146 -0
  462. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/schemas.js +2093 -0
  463. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/standard-schema.cjs +2 -0
  464. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/standard-schema.d.cts +126 -0
  465. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/standard-schema.d.ts +126 -0
  466. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/standard-schema.js +1 -0
  467. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/to-json-schema.cjs +446 -0
  468. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/to-json-schema.d.cts +114 -0
  469. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/to-json-schema.d.ts +114 -0
  470. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/to-json-schema.js +437 -0
  471. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/util.cjs +710 -0
  472. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/util.d.cts +199 -0
  473. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/util.d.ts +199 -0
  474. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/util.js +651 -0
  475. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/versions.cjs +8 -0
  476. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/versions.d.cts +5 -0
  477. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/versions.d.ts +5 -0
  478. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/core/versions.js +5 -0
  479. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/index.cjs +22 -0
  480. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/index.d.cts +3 -0
  481. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/index.d.ts +3 -0
  482. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/index.js +3 -0
  483. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ar.cjs +133 -0
  484. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ar.d.cts +5 -0
  485. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ar.d.ts +4 -0
  486. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ar.js +106 -0
  487. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/az.cjs +132 -0
  488. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/az.d.cts +5 -0
  489. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/az.d.ts +4 -0
  490. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/az.js +105 -0
  491. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/be.cjs +183 -0
  492. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/be.d.cts +5 -0
  493. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/be.d.ts +4 -0
  494. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/be.js +156 -0
  495. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/bg.cjs +147 -0
  496. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/bg.d.cts +5 -0
  497. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/bg.d.ts +4 -0
  498. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/bg.js +120 -0
  499. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ca.cjs +134 -0
  500. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ca.d.cts +5 -0
  501. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ca.d.ts +4 -0
  502. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ca.js +107 -0
  503. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/cs.cjs +138 -0
  504. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/cs.d.cts +5 -0
  505. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/cs.d.ts +4 -0
  506. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/cs.js +111 -0
  507. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/da.cjs +142 -0
  508. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/da.d.cts +5 -0
  509. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/da.d.ts +4 -0
  510. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/da.js +115 -0
  511. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/de.cjs +135 -0
  512. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/de.d.cts +5 -0
  513. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/de.d.ts +4 -0
  514. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/de.js +108 -0
  515. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/en.cjs +136 -0
  516. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/en.d.cts +5 -0
  517. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/en.d.ts +4 -0
  518. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/en.js +109 -0
  519. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/eo.cjs +136 -0
  520. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/eo.d.cts +5 -0
  521. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/eo.d.ts +4 -0
  522. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/eo.js +109 -0
  523. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/es.cjs +159 -0
  524. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/es.d.cts +5 -0
  525. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/es.d.ts +4 -0
  526. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/es.js +132 -0
  527. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fa.cjs +141 -0
  528. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fa.d.cts +5 -0
  529. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fa.d.ts +4 -0
  530. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fa.js +114 -0
  531. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fi.cjs +139 -0
  532. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fi.d.cts +5 -0
  533. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fi.d.ts +4 -0
  534. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fi.js +112 -0
  535. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr-CA.cjs +134 -0
  536. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr-CA.d.cts +5 -0
  537. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr-CA.d.ts +4 -0
  538. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr-CA.js +107 -0
  539. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr.cjs +135 -0
  540. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr.d.cts +5 -0
  541. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr.d.ts +4 -0
  542. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/fr.js +108 -0
  543. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/he.cjs +241 -0
  544. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/he.d.cts +5 -0
  545. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/he.d.ts +4 -0
  546. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/he.js +214 -0
  547. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hu.cjs +135 -0
  548. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hu.d.cts +5 -0
  549. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hu.d.ts +4 -0
  550. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hu.js +108 -0
  551. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hy.cjs +174 -0
  552. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hy.d.cts +5 -0
  553. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hy.d.ts +4 -0
  554. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/hy.js +147 -0
  555. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/id.cjs +133 -0
  556. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/id.d.cts +5 -0
  557. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/id.d.ts +4 -0
  558. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/id.js +106 -0
  559. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/index.cjs +104 -0
  560. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/index.d.cts +49 -0
  561. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/index.d.ts +49 -0
  562. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/index.js +49 -0
  563. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/is.cjs +136 -0
  564. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/is.d.cts +5 -0
  565. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/is.d.ts +4 -0
  566. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/is.js +109 -0
  567. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/it.cjs +135 -0
  568. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/it.d.cts +5 -0
  569. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/it.d.ts +4 -0
  570. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/it.js +108 -0
  571. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ja.cjs +134 -0
  572. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ja.d.cts +5 -0
  573. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ja.d.ts +4 -0
  574. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ja.js +107 -0
  575. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ka.cjs +139 -0
  576. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ka.d.cts +5 -0
  577. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ka.d.ts +4 -0
  578. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ka.js +112 -0
  579. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/kh.cjs +12 -0
  580. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/kh.d.cts +5 -0
  581. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/kh.d.ts +5 -0
  582. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/kh.js +5 -0
  583. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/km.cjs +137 -0
  584. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/km.d.cts +5 -0
  585. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/km.d.ts +4 -0
  586. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/km.js +110 -0
  587. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ko.cjs +138 -0
  588. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ko.d.cts +5 -0
  589. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ko.d.ts +4 -0
  590. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ko.js +111 -0
  591. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/lt.cjs +230 -0
  592. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/lt.d.cts +5 -0
  593. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/lt.d.ts +4 -0
  594. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/lt.js +203 -0
  595. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/mk.cjs +136 -0
  596. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/mk.d.cts +5 -0
  597. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/mk.d.ts +4 -0
  598. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/mk.js +109 -0
  599. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ms.cjs +134 -0
  600. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ms.d.cts +5 -0
  601. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ms.d.ts +4 -0
  602. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ms.js +107 -0
  603. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/nl.cjs +137 -0
  604. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/nl.d.cts +5 -0
  605. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/nl.d.ts +4 -0
  606. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/nl.js +110 -0
  607. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/no.cjs +135 -0
  608. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/no.d.cts +5 -0
  609. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/no.d.ts +4 -0
  610. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/no.js +108 -0
  611. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ota.cjs +136 -0
  612. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ota.d.cts +5 -0
  613. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ota.d.ts +4 -0
  614. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ota.js +109 -0
  615. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/package.json +6 -0
  616. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pl.cjs +136 -0
  617. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pl.d.cts +5 -0
  618. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pl.d.ts +4 -0
  619. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pl.js +109 -0
  620. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ps.cjs +141 -0
  621. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ps.d.cts +5 -0
  622. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ps.d.ts +4 -0
  623. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ps.js +114 -0
  624. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pt.cjs +135 -0
  625. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pt.d.cts +5 -0
  626. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pt.d.ts +4 -0
  627. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/pt.js +108 -0
  628. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ru.cjs +183 -0
  629. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ru.d.cts +5 -0
  630. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ru.d.ts +4 -0
  631. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ru.js +156 -0
  632. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sl.cjs +136 -0
  633. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sl.d.cts +5 -0
  634. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sl.d.ts +4 -0
  635. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sl.js +109 -0
  636. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sv.cjs +137 -0
  637. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sv.d.cts +5 -0
  638. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sv.d.ts +4 -0
  639. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/sv.js +110 -0
  640. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ta.cjs +137 -0
  641. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ta.d.cts +5 -0
  642. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ta.d.ts +4 -0
  643. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ta.js +110 -0
  644. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/th.cjs +137 -0
  645. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/th.d.cts +5 -0
  646. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/th.d.ts +4 -0
  647. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/th.js +110 -0
  648. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/tr.cjs +132 -0
  649. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/tr.d.cts +5 -0
  650. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/tr.d.ts +4 -0
  651. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/tr.js +105 -0
  652. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ua.cjs +12 -0
  653. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ua.d.cts +5 -0
  654. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ua.d.ts +5 -0
  655. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ua.js +5 -0
  656. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uk.cjs +135 -0
  657. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uk.d.cts +5 -0
  658. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uk.d.ts +4 -0
  659. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uk.js +108 -0
  660. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ur.cjs +137 -0
  661. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ur.d.cts +5 -0
  662. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ur.d.ts +4 -0
  663. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/ur.js +110 -0
  664. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uz.cjs +136 -0
  665. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uz.d.cts +5 -0
  666. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uz.d.ts +4 -0
  667. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/uz.js +109 -0
  668. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/vi.cjs +135 -0
  669. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/vi.d.cts +5 -0
  670. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/vi.d.ts +4 -0
  671. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/vi.js +108 -0
  672. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/yo.cjs +134 -0
  673. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/yo.d.cts +5 -0
  674. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/yo.d.ts +4 -0
  675. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/yo.js +107 -0
  676. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-CN.cjs +136 -0
  677. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-CN.d.cts +5 -0
  678. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-CN.d.ts +4 -0
  679. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-CN.js +109 -0
  680. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-TW.cjs +134 -0
  681. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-TW.d.cts +5 -0
  682. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-TW.d.ts +4 -0
  683. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/locales/zh-TW.js +107 -0
  684. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/checks.cjs +34 -0
  685. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/checks.d.cts +1 -0
  686. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/checks.d.ts +1 -0
  687. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/checks.js +1 -0
  688. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/coerce.cjs +52 -0
  689. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/coerce.d.cts +7 -0
  690. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/coerce.d.ts +7 -0
  691. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/coerce.js +22 -0
  692. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/external.cjs +63 -0
  693. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/external.d.cts +12 -0
  694. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/external.d.ts +12 -0
  695. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/external.js +14 -0
  696. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/index.cjs +32 -0
  697. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/index.d.cts +3 -0
  698. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/index.d.ts +3 -0
  699. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/index.js +3 -0
  700. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/iso.cjs +64 -0
  701. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/iso.d.cts +22 -0
  702. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/iso.d.ts +22 -0
  703. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/iso.js +34 -0
  704. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/package.json +6 -0
  705. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/parse.cjs +16 -0
  706. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/parse.d.cts +1 -0
  707. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/parse.d.ts +1 -0
  708. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/parse.js +1 -0
  709. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/schemas.cjs +1046 -0
  710. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/schemas.d.cts +427 -0
  711. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/schemas.d.ts +427 -0
  712. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/mini/schemas.js +925 -0
  713. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4/package.json +6 -0
  714. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4-mini/index.cjs +32 -0
  715. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4-mini/index.d.cts +3 -0
  716. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4-mini/index.d.ts +3 -0
  717. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4-mini/index.js +3 -0
  718. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/node_modules/zod/v4-mini/package.json +6 -0
  719. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/package.json +37 -0
  720. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/attachment-naming.test.ts +104 -0
  721. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +36 -0
  722. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +299 -0
  723. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/conversation-handle.test.ts +316 -0
  724. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +280 -0
  725. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/package-boundary.test.ts +151 -0
  726. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  727. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  728. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/secret-detection.test.ts +449 -0
  729. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
  730. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
  731. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/attachment-naming.ts +118 -0
  732. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/channels.ts +92 -0
  733. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/client-metadata.ts +46 -0
  734. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/conversation-handle.ts +246 -0
  735. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/credential-rpc.ts +21 -0
  736. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/error.ts +26 -0
  737. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/handles.ts +212 -0
  738. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/index.ts +32 -0
  739. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/ingress.ts +175 -0
  740. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  741. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +816 -0
  742. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/rpc.ts +408 -0
  743. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/secret-detection.ts +468 -0
  744. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
  745. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/transport.ts +133 -0
  746. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/trust-rules.ts +116 -0
  747. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +73 -0
  748. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
  749. package/node_modules/@vellumai/local-mode/node_modules/@vellumai/service-contracts/tsconfig.json +20 -0
  750. package/node_modules/@vellumai/local-mode/package.json +3 -1
  751. package/node_modules/@vellumai/local-mode/src/__tests__/guardian-token.test.ts +45 -0
  752. package/node_modules/@vellumai/local-mode/src/__tests__/package-boundary.test.ts +32 -14
  753. package/node_modules/@vellumai/local-mode/src/__tests__/pair.test.ts +1535 -155
  754. package/node_modules/@vellumai/local-mode/src/__tests__/status.test.ts +71 -1
  755. package/node_modules/@vellumai/local-mode/src/avatar.test.ts +404 -0
  756. package/node_modules/@vellumai/local-mode/src/avatar.ts +151 -0
  757. package/node_modules/@vellumai/local-mode/src/devices.test.ts +62 -0
  758. package/node_modules/@vellumai/local-mode/src/devices.ts +12 -0
  759. package/node_modules/@vellumai/local-mode/src/guardian-token.ts +27 -34
  760. package/node_modules/@vellumai/local-mode/src/index.ts +26 -3
  761. package/node_modules/@vellumai/local-mode/src/lockfile-contract.ts +1 -1
  762. package/node_modules/@vellumai/local-mode/src/lockfile.test.ts +1 -0
  763. package/node_modules/@vellumai/local-mode/src/lockfile.ts +6 -3
  764. package/node_modules/@vellumai/local-mode/src/pair.ts +1099 -129
  765. package/node_modules/@vellumai/local-mode/src/status.ts +67 -22
  766. package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +37 -0
  767. package/node_modules/@vellumai/service-contracts/src/__tests__/remote-web-pairing.test.ts +389 -0
  768. package/node_modules/@vellumai/service-contracts/src/ingress.ts +22 -0
  769. package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +508 -18
  770. package/node_modules/@vellumai/service-contracts/src/rpc.ts +174 -0
  771. package/package.json +3 -1
  772. package/src/__tests__/connect-import.test.ts +512 -172
  773. package/src/__tests__/detached-process.test.ts +152 -0
  774. package/src/__tests__/devices.test.ts +12 -0
  775. package/src/__tests__/hatch-provider-secrets.test.ts +95 -0
  776. package/src/__tests__/pair.test.ts +319 -305
  777. package/src/__tests__/provider-secrets.test.ts +158 -0
  778. package/src/__tests__/tunnel.test.ts +200 -23
  779. package/src/__tests__/wake.test.ts +51 -1
  780. package/src/adapters/install.sh +2 -1
  781. package/src/adapters/openclaw.ts +2 -1
  782. package/src/commands/client.ts +81 -58
  783. package/src/commands/connect/import.ts +248 -52
  784. package/src/commands/connect.ts +1 -1
  785. package/src/commands/devices.ts +4 -0
  786. package/src/commands/pair.ts +173 -298
  787. package/src/commands/tunnel.ts +162 -54
  788. package/src/commands/voice.ts +370 -0
  789. package/src/components/DefaultMainScreen.tsx +0 -144
  790. package/src/index.ts +5 -2
  791. package/src/lib/detached-process.ts +81 -0
  792. package/src/lib/docker.ts +21 -4
  793. package/src/lib/guardian-token.ts +19 -2
  794. package/src/lib/hatch-local.ts +23 -5
  795. package/src/lib/live-voice/client.test.ts +316 -0
  796. package/src/lib/live-voice/client.ts +363 -0
  797. package/src/lib/live-voice/connection.ts +133 -0
  798. package/src/lib/live-voice/pcm-player.test.ts +101 -0
  799. package/src/lib/live-voice/pcm-player.ts +356 -0
  800. package/src/lib/live-voice/protocol.test.ts +53 -0
  801. package/src/lib/live-voice/protocol.ts +193 -0
  802. package/src/lib/local.ts +2 -1
  803. package/src/lib/provider-secrets.ts +195 -0
  804. package/src/lib/shell-arg.test.ts +24 -0
  805. package/src/lib/shell-arg.ts +17 -0
  806. package/src/lib/tunnel-edge.ts +31 -2
  807. package/src/shared/provider-env-vars.ts +1 -0
@@ -3,26 +3,68 @@ import fs from "node:fs";
3
3
  import os from "node:os";
4
4
  import path from "node:path";
5
5
 
6
+ import { pairingSessionSurvives } from "@vellumai/service-contracts/remote-web-pairing";
7
+
6
8
  import { guardianTokenPath } from "../config";
7
9
  import {
10
+ checkPairedAssistantName,
8
11
  connectImport,
9
- decodePairBundle,
10
- MAX_PAIR_BUNDLE_LENGTH,
11
12
  pairAssistant,
12
- type PairBundle,
13
+ pairingCancel,
14
+ pairingPoll,
15
+ pairingStart,
16
+ type PairedAssistantCredentials,
13
17
  } from "../pair";
14
18
 
15
19
  let tmpDir: string;
16
20
  let lockfilePath: string;
17
21
  let configDir: string;
18
22
 
23
+ /** One recorded outbound pairing request. */
24
+ interface FetchCall {
25
+ url: string;
26
+ body: Record<string, unknown>;
27
+ redirect: string | undefined;
28
+ signal: AbortSignal | undefined;
29
+ }
30
+
31
+ let fetchCalls: FetchCall[];
32
+ let respond: (call: FetchCall) => Response | Promise<Response>;
33
+ const realFetch = globalThis.fetch;
34
+
19
35
  beforeEach(() => {
20
36
  tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "vellum-pair-"));
21
37
  lockfilePath = path.join(tmpDir, "lockfile.json");
22
38
  configDir = path.join(tmpDir, "config");
39
+ fetchCalls = [];
40
+ respond = () => new Response(null, { status: 500 });
41
+ globalThis.fetch = (async (
42
+ input: unknown,
43
+ init?: { body?: unknown; redirect?: string; signal?: AbortSignal },
44
+ ): Promise<Response> => {
45
+ const call: FetchCall = {
46
+ url: String(input),
47
+ body: JSON.parse(String(init?.body ?? "{}")) as Record<string, unknown>,
48
+ redirect: init?.redirect,
49
+ signal: init?.signal,
50
+ };
51
+ fetchCalls.push(call);
52
+ const response = await respond(call);
53
+ // Mirror the platform: a 3xx under `redirect: "error"` rejects instead of
54
+ // being followed.
55
+ if (
56
+ init?.redirect === "error" &&
57
+ response.status >= 300 &&
58
+ response.status < 400
59
+ ) {
60
+ throw new TypeError("unexpected redirect");
61
+ }
62
+ return response;
63
+ }) as unknown as typeof fetch;
23
64
  });
24
65
 
25
66
  afterEach(() => {
67
+ globalThis.fetch = realFetch;
26
68
  fs.rmSync(tmpDir, { recursive: true, force: true });
27
69
  });
28
70
 
@@ -33,119 +75,131 @@ const writeLockfile = (data: Record<string, unknown>): void => {
33
75
  const readLockfileFromDisk = (): Record<string, unknown> =>
34
76
  JSON.parse(fs.readFileSync(lockfilePath, "utf-8")) as Record<string, unknown>;
35
77
 
78
+ const readGuardianToken = (assistantId: string): Record<string, unknown> =>
79
+ JSON.parse(
80
+ fs.readFileSync(guardianTokenPath(configDir, assistantId), "utf-8"),
81
+ ) as Record<string, unknown>;
82
+
36
83
  const encodeBundle = (obj: Record<string, unknown>): string =>
37
84
  Buffer.from(JSON.stringify(obj)).toString("base64");
38
85
 
39
- const bundle = (overrides: Partial<PairBundle> = {}): PairBundle => ({
86
+ const credentials = (
87
+ overrides: Partial<PairedAssistantCredentials> = {},
88
+ ): PairedAssistantCredentials => ({
40
89
  gatewayUrl: "http://10.0.0.5:7830",
41
90
  token: "test-token",
42
- assistantId: "self",
43
91
  deviceId: "dev-aaa",
44
92
  ...overrides,
45
93
  });
46
94
 
95
+ /** The nameless id the default credentials' gateway host derives. */
96
+ const DEFAULT_PAIRED_ID = "paired-10-0-0-5-7830";
97
+
47
98
  /** A syntactically valid JWT whose `exp` is 2030-01-01T00:00:00Z. */
48
99
  const JWT_EXP_S = 1893456000;
49
100
  const jwtWithExp = `h.${Buffer.from(JSON.stringify({ exp: JWT_EXP_S })).toString("base64")}.s`;
50
101
 
51
- describe("decodePairBundle", () => {
52
- test("decodes a full bundle, preserving a numeric refreshTokenExpiresAt", () => {
53
- const result = decodePairBundle(
54
- encodeBundle({
55
- gatewayUrl: "https://tunnel.example.com",
56
- token: "tok",
57
- assistantId: "self",
58
- deviceId: "dev-1",
59
- refreshToken: "refresh",
60
- refreshTokenExpiresAt: 1893456000000,
61
- refreshAfter: "2026-07-01T00:00:00.000Z",
62
- }),
63
- );
64
-
65
- expect(result).toEqual({
66
- ok: true,
67
- bundle: {
68
- gatewayUrl: "https://tunnel.example.com",
69
- token: "tok",
70
- assistantId: "self",
71
- deviceId: "dev-1",
72
- refreshToken: "refresh",
73
- refreshTokenExpiresAt: 1893456000000,
74
- refreshAfter: "2026-07-01T00:00:00.000Z",
75
- },
76
- });
102
+ const json = (status: number, body: unknown): Response =>
103
+ new Response(JSON.stringify(body), {
104
+ status,
105
+ headers: { "Content-Type": "application/json" },
77
106
  });
78
107
 
79
- test("drops malformed optional fields instead of failing", () => {
80
- const result = decodePairBundle(
81
- encodeBundle({
82
- gatewayUrl: "http://10.0.0.5:7830",
83
- token: "tok",
84
- deviceId: 42,
85
- refreshToken: { nope: true },
86
- refreshAfter: 7,
87
- }),
88
- );
108
+ /**
109
+ * A reply whose JSON is written out literally, so a value `JSON.stringify`
110
+ * cannot express reaches the parser exactly as a remote sends it. `1e400`
111
+ * parses back as Infinity, which is how a hostile or broken assistant names an
112
+ * unbounded cadence.
113
+ */
114
+ const rawJson = (status: number, text: string): Response =>
115
+ new Response(text, {
116
+ status,
117
+ headers: { "Content-Type": "application/json" },
118
+ });
89
119
 
90
- expect(result).toEqual({
91
- ok: true,
92
- bundle: {
93
- gatewayUrl: "http://10.0.0.5:7830",
94
- token: "tok",
95
- assistantId: undefined,
96
- deviceId: undefined,
97
- refreshToken: undefined,
98
- refreshTokenExpiresAt: undefined,
99
- refreshAfter: undefined,
120
+ /** A 3xx pointing somewhere `parsePairingAddress` would never have allowed. */
121
+ const redirectTo = (target: string): Response =>
122
+ new Response(null, { status: 307, headers: { Location: target } });
123
+
124
+ /**
125
+ * A body streamed in 8 KiB chunks, pulled on demand, so a reader that stops
126
+ * early never buffers the rest. `onPull` counts the chunks actually asked for.
127
+ */
128
+ const streamedBody = (chunkCount: number, onPull: () => void): Response => {
129
+ const chunk = new Uint8Array(8 * 1024).fill(0x20);
130
+ let sent = 0;
131
+ return new Response(
132
+ new ReadableStream<Uint8Array>({
133
+ pull(controller) {
134
+ onPull();
135
+ if (sent >= chunkCount) {
136
+ controller.close();
137
+ return;
138
+ }
139
+ sent += 1;
140
+ controller.enqueue(chunk);
100
141
  },
101
- });
102
- });
142
+ }),
143
+ { status: 200, headers: { "Content-Type": "application/json" } },
144
+ );
145
+ };
103
146
 
104
- test("rejects garbage base64 and JSON non-objects without throwing", () => {
105
- for (const encoded of [
106
- "not-valid-base64!!",
107
- Buffer.from(JSON.stringify("just a string")).toString("base64"),
108
- Buffer.from("{truncated").toString("base64"),
109
- ]) {
110
- const result = decodePairBundle(encoded);
111
- expect(result.ok).toBe(false);
112
- if (result.ok) {
113
- continue;
114
- }
115
- expect(typeof result.error).toBe("string");
116
- }
117
- });
147
+ const challengeBody = (
148
+ overrides: Record<string, unknown> = {},
149
+ ): Record<string, unknown> => ({
150
+ deviceCode: "device-code-abc",
151
+ userCode: "ABCD-EFGH",
152
+ verificationUri: "https://gw.example.com/assistant/pair",
153
+ expiresAt: new Date(Date.now() + 600_000).toISOString(),
154
+ expiresInSeconds: 600,
155
+ intervalSeconds: 3,
156
+ ...overrides,
157
+ });
118
158
 
119
- test("rejects a bundle missing token or gatewayUrl", () => {
120
- expect(decodePairBundle(encodeBundle({ token: "tok" })).ok).toBe(false);
121
- expect(
122
- decodePairBundle(encodeBundle({ gatewayUrl: "http://h" })).ok,
123
- ).toBe(false);
124
- });
159
+ const approvedBody = (
160
+ overrides: Record<string, unknown> = {},
161
+ ): Record<string, unknown> => ({
162
+ status: "approved",
163
+ accessToken: "acc-tok",
164
+ accessTokenExpiresAt: new Date(Date.now() + 3_600_000).toISOString(),
165
+ refreshAfter: new Date(Date.now() + 1_800_000).toISOString(),
166
+ guardianId: "guardian-1",
167
+ assistantId: "self",
168
+ refreshToken: "refresh-tok",
169
+ refreshTokenExpiresAt: new Date(Date.now() + 86_400_000).toISOString(),
170
+ ...overrides,
171
+ });
125
172
 
126
- test("rejects a non-http(s) or relative gatewayUrl", () => {
127
- expect(
128
- decodePairBundle(encodeBundle({ gatewayUrl: "ftp://nope", token: "t" }))
129
- .ok,
130
- ).toBe(false);
131
- expect(
132
- decodePairBundle(encodeBundle({ gatewayUrl: "/relative", token: "t" }))
133
- .ok,
134
- ).toBe(false);
135
- });
173
+ const pendingBody = (
174
+ overrides: Record<string, unknown> = {},
175
+ ): Record<string, unknown> => ({
176
+ status: "pending",
177
+ expiresAt: new Date(Date.now() + 500_000).toISOString(),
178
+ intervalSeconds: 7,
179
+ ...overrides,
136
180
  });
137
181
 
182
+ /** Resolve once `count` pairing requests are actually in flight. */
183
+ const requestsInFlight = async (count: number): Promise<void> => {
184
+ while (fetchCalls.length < count) {
185
+ await new Promise((resolve) => setTimeout(resolve, 0));
186
+ }
187
+ };
188
+
189
+ const sleep = (ms: number): Promise<void> =>
190
+ new Promise((resolve) => setTimeout(resolve, ms));
191
+
138
192
  describe("pairAssistant", () => {
139
193
  test("writes the lockfile entry and guardian token with the exact CLI shapes and modes", () => {
140
194
  const result = pairAssistant([lockfilePath], configDir, {
141
- bundle: bundle({ token: jwtWithExp, deviceId: "dev-aaa" }),
195
+ credentials: credentials({ token: jwtWithExp, deviceId: "dev-aaa" }),
142
196
  });
143
197
 
144
198
  expect(result.ok).toBe(true);
145
199
  if (!result.ok) {
146
200
  return;
147
201
  }
148
- expect(result.assistantId).toBe("paired-dev-aaa");
202
+ expect(result.assistantId).toBe(DEFAULT_PAIRED_ID);
149
203
  expect(result.updated).toBe(false);
150
204
  expect(result.accessOnly).toBe(true);
151
205
 
@@ -154,7 +208,7 @@ describe("pairAssistant", () => {
154
208
  const onDisk = readLockfileFromDisk();
155
209
  const entry = (onDisk.assistants as Array<Record<string, unknown>>)[0]!;
156
210
  expect(entry).toEqual({
157
- assistantId: "paired-dev-aaa",
211
+ assistantId: DEFAULT_PAIRED_ID,
158
212
  name: "paired (10.0.0.5:7830)",
159
213
  runtimeUrl: "http://10.0.0.5:7830",
160
214
  cloud: "paired",
@@ -172,7 +226,7 @@ describe("pairAssistant", () => {
172
226
  // Importing never reassigns the active assistant.
173
227
  expect(onDisk.activeAssistant ?? null).toBeNull();
174
228
 
175
- const tokenPath = guardianTokenPath(configDir, "paired-dev-aaa");
229
+ const tokenPath = guardianTokenPath(configDir, DEFAULT_PAIRED_ID);
176
230
  const raw = fs.readFileSync(tokenPath, "utf-8");
177
231
  expect(raw.endsWith("\n")).toBe(true);
178
232
  const token = JSON.parse(raw) as Record<string, unknown>;
@@ -188,9 +242,7 @@ describe("pairAssistant", () => {
188
242
  deviceId: "dev-aaa",
189
243
  pairedGatewayUrl: "http://10.0.0.5:7830",
190
244
  });
191
- expect(new Date(leasedAt as string).toISOString()).toBe(
192
- leasedAt as string,
193
- );
245
+ expect(new Date(leasedAt as string).toISOString()).toBe(leasedAt as string);
194
246
  expect(Object.keys(token)).toEqual([
195
247
  "guardianPrincipalId",
196
248
  "accessToken",
@@ -211,13 +263,11 @@ describe("pairAssistant", () => {
211
263
  test("falls back to now+24h expiry for a non-JWT access token", () => {
212
264
  const before = Date.now();
213
265
  const result = pairAssistant([lockfilePath], configDir, {
214
- bundle: bundle({ token: "opaque-token", deviceId: "dev-exp" }),
266
+ credentials: credentials({ token: "opaque-token", deviceId: "dev-exp" }),
215
267
  });
216
268
  expect(result.ok).toBe(true);
217
269
 
218
- const token = JSON.parse(
219
- fs.readFileSync(guardianTokenPath(configDir, "paired-dev-exp"), "utf-8"),
220
- ) as Record<string, unknown>;
270
+ const token = readGuardianToken(DEFAULT_PAIRED_ID);
221
271
  const dayMs = 24 * 60 * 60 * 1000;
222
272
  expect(token.accessTokenExpiresAt as number).toBeGreaterThanOrEqual(
223
273
  before + dayMs,
@@ -229,7 +279,7 @@ describe("pairAssistant", () => {
229
279
 
230
280
  test("persists the refresh credential and reports accessOnly: false", () => {
231
281
  const result = pairAssistant([lockfilePath], configDir, {
232
- bundle: bundle({
282
+ credentials: credentials({
233
283
  gatewayUrl: "https://gw.example.com",
234
284
  deviceId: "dev-refresh",
235
285
  refreshToken: "refresh-tok",
@@ -243,18 +293,29 @@ describe("pairAssistant", () => {
243
293
  return;
244
294
  }
245
295
  expect(result.accessOnly).toBe(false);
246
- const token = JSON.parse(
247
- fs.readFileSync(
248
- guardianTokenPath(configDir, "paired-dev-refresh"),
249
- "utf-8",
250
- ),
251
- ) as Record<string, unknown>;
296
+ const token = readGuardianToken("paired-gw-example-com");
252
297
  expect(token.refreshToken).toBe("refresh-tok");
253
298
  expect(token.refreshTokenExpiresAt).toBe("2027-01-01T00:00:00.000Z");
254
299
  expect(token.refreshAfter).toBe("2026-07-01T00:00:00.000Z");
255
300
  });
256
301
 
257
- test("refuses loopback gateway URLs so a bundle can't alias local services", () => {
302
+ test("preserves a numeric (epoch-ms) refreshTokenExpiresAt", () => {
303
+ const result = pairAssistant([lockfilePath], configDir, {
304
+ credentials: credentials({
305
+ gatewayUrl: "https://gw.example.com",
306
+ deviceId: "dev-num",
307
+ refreshToken: "refresh-tok",
308
+ refreshTokenExpiresAt: 1893456000000,
309
+ }),
310
+ });
311
+
312
+ expect(result.ok).toBe(true);
313
+ expect(
314
+ readGuardianToken("paired-gw-example-com").refreshTokenExpiresAt,
315
+ ).toBe(1893456000000);
316
+ });
317
+
318
+ test("refuses loopback gateway URLs so a pairing can't alias local services", () => {
258
319
  // A stored loopback runtimeUrl would otherwise read as a local gateway
259
320
  // (e.g. to the loopback proxy allowlist), aliasing arbitrary local ports.
260
321
  for (const gatewayUrl of [
@@ -271,7 +332,7 @@ describe("pairAssistant", () => {
271
332
  "http://[::ffff:0.0.0.0]:7830",
272
333
  ]) {
273
334
  const result = pairAssistant([lockfilePath], configDir, {
274
- bundle: bundle({ gatewayUrl, deviceId: "dev-loop" }),
335
+ credentials: credentials({ gatewayUrl, deviceId: "dev-loop" }),
275
336
  });
276
337
 
277
338
  expect(result.ok).toBe(false);
@@ -281,11 +342,25 @@ describe("pairAssistant", () => {
281
342
  expect(result.status).toBe(422);
282
343
  expect(result.error).toContain("non-loopback");
283
344
  }
284
- // Nothing was written for any refused bundle.
345
+ // Nothing was written for any refused pairing.
285
346
  expect(fs.existsSync(lockfilePath)).toBe(false);
286
- expect(
287
- fs.existsSync(guardianTokenPath(configDir, "paired-dev-loop")),
288
- ).toBe(false);
347
+ expect(fs.existsSync(path.join(configDir, "assistants"))).toBe(false);
348
+ });
349
+
350
+ test("a loopback refusal points at an address that can still work", () => {
351
+ // Private-network literals are refused before a pairing is ever minted, so
352
+ // advice to retry with a LAN address would send the user into a guaranteed
353
+ // second failure.
354
+ const result = pairAssistant([lockfilePath], configDir, {
355
+ credentials: credentials({ gatewayUrl: "http://localhost:7830" }),
356
+ });
357
+
358
+ expect(result.ok).toBe(false);
359
+ if (result.ok) {
360
+ return;
361
+ }
362
+ expect(result.error).toContain("tunnel or public https address");
363
+ expect(result.error).not.toContain("LAN");
289
364
  });
290
365
 
291
366
  test("a non-loopback plaintext http gateway is access-only even with a refresh token", () => {
@@ -293,7 +368,7 @@ describe("pairAssistant", () => {
293
368
  // LAN URLs, so the pairing can never renew; report it access-only so the
294
369
  // expiry warning shows.
295
370
  const result = pairAssistant([lockfilePath], configDir, {
296
- bundle: bundle({
371
+ credentials: credentials({
297
372
  gatewayUrl: "http://10.0.0.5:7830",
298
373
  deviceId: "dev-lan",
299
374
  refreshToken: "refresh-tok",
@@ -310,7 +385,7 @@ describe("pairAssistant", () => {
310
385
 
311
386
  test("a name is slugified into the id while the entry keeps the raw name", () => {
312
387
  const result = pairAssistant([lockfilePath], configDir, {
313
- bundle: bundle(),
388
+ credentials: credentials(),
314
389
  name: "Desk Box",
315
390
  });
316
391
 
@@ -327,7 +402,7 @@ describe("pairAssistant", () => {
327
402
 
328
403
  test("a name with no alphanumerics is refused with a 400", () => {
329
404
  const result = pairAssistant([lockfilePath], configDir, {
330
- bundle: bundle(),
405
+ credentials: credentials(),
331
406
  name: "!!!",
332
407
  });
333
408
 
@@ -339,25 +414,26 @@ describe("pairAssistant", () => {
339
414
  expect(fs.existsSync(lockfilePath)).toBe(false);
340
415
  });
341
416
 
342
- test("a malicious deviceId is slugified out of the path", () => {
417
+ test("the derived id is path-safe whatever the deviceId carries", () => {
343
418
  const result = pairAssistant([lockfilePath], configDir, {
344
- bundle: bundle({ deviceId: "-/../../tmp/x" }),
419
+ credentials: credentials({ deviceId: "-/../../tmp/x" }),
345
420
  });
346
421
 
347
422
  expect(result.ok).toBe(true);
348
423
  if (!result.ok) {
349
424
  return;
350
425
  }
351
- expect(result.assistantId).not.toContain("/");
352
- expect(result.assistantId).not.toContain("..");
426
+ // The id keys on the gateway host, so an untrusted deviceId never reaches
427
+ // the token path at all.
428
+ expect(result.assistantId).toBe(DEFAULT_PAIRED_ID);
353
429
  expect(
354
430
  fs.existsSync(guardianTokenPath(configDir, result.assistantId)),
355
431
  ).toBe(true);
356
432
  });
357
433
 
358
- test("a missing deviceId falls back to a random path-safe id", () => {
434
+ test("a gatewayUrl with no host falls back to a random path-safe id", () => {
359
435
  const result = pairAssistant([lockfilePath], configDir, {
360
- bundle: bundle({ deviceId: undefined }),
436
+ credentials: credentials({ gatewayUrl: "mailto:ops@example.com" }),
361
437
  });
362
438
 
363
439
  expect(result.ok).toBe(true);
@@ -367,6 +443,156 @@ describe("pairAssistant", () => {
367
443
  expect(result.assistantId).toMatch(/^paired-[A-Za-z0-9_-]{21}$/);
368
444
  });
369
445
 
446
+ test("re-pairing with no name updates the entry instead of adding another", () => {
447
+ // Every attempt mints a fresh device id, so keying the default id on it
448
+ // would strand the previous pairing in the lockfile (and its guardian
449
+ // token on disk) every time a user re-paired the same assistant.
450
+ const first = pairAssistant([lockfilePath], configDir, {
451
+ credentials: credentials({ deviceId: "dev-one", token: "t1" }),
452
+ });
453
+ expect(first.ok).toBe(true);
454
+ if (!first.ok) {
455
+ return;
456
+ }
457
+ expect(first.updated).toBe(false);
458
+
459
+ const second = pairAssistant([lockfilePath], configDir, {
460
+ credentials: credentials({ deviceId: "dev-two", token: "t2" }),
461
+ });
462
+
463
+ expect(second.ok).toBe(true);
464
+ if (!second.ok) {
465
+ return;
466
+ }
467
+ expect(second.assistantId).toBe(first.assistantId);
468
+ expect(second.updated).toBe(true);
469
+
470
+ const assistants = readLockfileFromDisk().assistants as Array<
471
+ Record<string, unknown>
472
+ >;
473
+ expect(assistants).toHaveLength(1);
474
+ // One credential on disk, holding the newest pairing's device.
475
+ expect(fs.readdirSync(path.join(configDir, "assistants"))).toEqual([
476
+ DEFAULT_PAIRED_ID,
477
+ ]);
478
+ const token = readGuardianToken(DEFAULT_PAIRED_ID);
479
+ expect(token.accessToken).toBe("t2");
480
+ expect(token.deviceId).toBe("dev-two");
481
+ });
482
+
483
+ test("two path-prefixed deployments on one host get distinct ids", () => {
484
+ // `normalizePairingBaseUrl` keeps a deployment path prefix, so these are
485
+ // two different assistants reached through one host. An id keyed on the
486
+ // host alone would read the second nameless import as a re-pair of the
487
+ // first: it would take over the entry, overwrite the guardian token, and
488
+ // leave the user unable to reach the first assistant at all.
489
+ const first = pairAssistant([lockfilePath], configDir, {
490
+ credentials: credentials({
491
+ gatewayUrl: "https://gw.example.com/assistant-1",
492
+ deviceId: "dev-one",
493
+ token: "t1",
494
+ }),
495
+ });
496
+ const second = pairAssistant([lockfilePath], configDir, {
497
+ credentials: credentials({
498
+ gatewayUrl: "https://gw.example.com/assistant-2",
499
+ deviceId: "dev-two",
500
+ token: "t2",
501
+ }),
502
+ });
503
+
504
+ expect(first.ok).toBe(true);
505
+ expect(second.ok).toBe(true);
506
+ if (!first.ok || !second.ok) {
507
+ return;
508
+ }
509
+ expect(first.assistantId).toBe("paired-gw-example-com-assistant-1");
510
+ expect(second.assistantId).toBe("paired-gw-example-com-assistant-2");
511
+ // Neither import read as a re-pair of the other.
512
+ expect(first.updated).toBe(false);
513
+ expect(second.updated).toBe(false);
514
+
515
+ // Both pairings survive: two entries, two guardian tokens, each still
516
+ // holding its own credential and address.
517
+ const assistants = readLockfileFromDisk().assistants as Array<
518
+ Record<string, unknown>
519
+ >;
520
+ expect(assistants).toHaveLength(2);
521
+ expect(assistants.map((a) => a.runtimeUrl)).toEqual([
522
+ "https://gw.example.com/assistant-1",
523
+ "https://gw.example.com/assistant-2",
524
+ ]);
525
+ expect(readGuardianToken(first.assistantId).accessToken).toBe("t1");
526
+ expect(readGuardianToken(second.assistantId).accessToken).toBe("t2");
527
+ expect(readGuardianToken(first.assistantId).deviceId).toBe("dev-one");
528
+ // The display names distinguish them too, or the chooser would show one
529
+ // entry twice under the same label.
530
+ expect(assistants.map((a) => a.name)).toEqual([
531
+ "paired (gw.example.com/assistant-1)",
532
+ "paired (gw.example.com/assistant-2)",
533
+ ]);
534
+ });
535
+
536
+ test("re-pairing a path-prefixed deployment still updates in place", () => {
537
+ // The prefix is part of the identity, not a nonce: the same address twice
538
+ // is the same assistant, so the second import updates rather than adds.
539
+ const first = pairAssistant([lockfilePath], configDir, {
540
+ credentials: credentials({
541
+ gatewayUrl: "https://gw.example.com/assistant-1",
542
+ deviceId: "dev-one",
543
+ token: "t1",
544
+ }),
545
+ });
546
+ const second = pairAssistant([lockfilePath], configDir, {
547
+ credentials: credentials({
548
+ gatewayUrl: "https://gw.example.com/assistant-1",
549
+ deviceId: "dev-two",
550
+ token: "t2",
551
+ }),
552
+ });
553
+
554
+ expect(first.ok).toBe(true);
555
+ expect(second.ok).toBe(true);
556
+ if (!first.ok || !second.ok) {
557
+ return;
558
+ }
559
+ expect(second.assistantId).toBe(first.assistantId);
560
+ expect(second.updated).toBe(true);
561
+ expect(readLockfileFromDisk().assistants).toHaveLength(1);
562
+ expect(readGuardianToken(first.assistantId).accessToken).toBe("t2");
563
+ });
564
+
565
+ test("a trailing slash names the same deployment as no path at all", () => {
566
+ const bare = pairAssistant([lockfilePath], configDir, {
567
+ credentials: credentials({ gatewayUrl: "https://gw.example.com" }),
568
+ });
569
+ const slashed = pairAssistant([lockfilePath], configDir, {
570
+ credentials: credentials({ gatewayUrl: "https://gw.example.com/" }),
571
+ });
572
+
573
+ expect(bare.ok).toBe(true);
574
+ expect(slashed.ok).toBe(true);
575
+ if (!bare.ok || !slashed.ok) {
576
+ return;
577
+ }
578
+ expect(bare.assistantId).toBe("paired-gw-example-com");
579
+ expect(slashed.assistantId).toBe(bare.assistantId);
580
+ expect(slashed.updated).toBe(true);
581
+ });
582
+
583
+ test("a --name still wins over the gateway-derived default", () => {
584
+ const result = pairAssistant([lockfilePath], configDir, {
585
+ credentials: credentials(),
586
+ name: "Desk",
587
+ });
588
+
589
+ expect(result.ok).toBe(true);
590
+ if (!result.ok) {
591
+ return;
592
+ }
593
+ expect(result.assistantId).toBe("desk");
594
+ });
595
+
370
596
  test("refuses to clobber an existing non-paired assistant", () => {
371
597
  writeLockfile({
372
598
  assistants: [
@@ -380,7 +606,7 @@ describe("pairAssistant", () => {
380
606
  });
381
607
 
382
608
  const result = pairAssistant([lockfilePath], configDir, {
383
- bundle: bundle(),
609
+ credentials: credentials(),
384
610
  name: "desk",
385
611
  });
386
612
 
@@ -401,18 +627,20 @@ describe("pairAssistant", () => {
401
627
  expect(fs.existsSync(guardianTokenPath(configDir, "desk"))).toBe(false);
402
628
  });
403
629
 
404
- test("re-importing over an existing paired entry updates it in place", () => {
630
+ test("re-importing under the same name updates it in place, address and all", () => {
405
631
  const first = pairAssistant([lockfilePath], configDir, {
406
- bundle: bundle({ deviceId: "dev-re", token: "t1" }),
632
+ credentials: credentials({ deviceId: "dev-re", token: "t1" }),
633
+ name: "desk",
407
634
  });
408
635
  expect(first.ok).toBe(true);
409
636
 
410
637
  const second = pairAssistant([lockfilePath], configDir, {
411
- bundle: bundle({
638
+ credentials: credentials({
412
639
  deviceId: "dev-re",
413
640
  token: "t2",
414
641
  gatewayUrl: "https://new.example.com",
415
642
  }),
643
+ name: "desk",
416
644
  });
417
645
 
418
646
  expect(second.ok).toBe(true);
@@ -425,10 +653,37 @@ describe("pairAssistant", () => {
425
653
  >;
426
654
  expect(assistants).toHaveLength(1);
427
655
  expect(assistants[0]!.runtimeUrl).toBe("https://new.example.com");
428
- const token = JSON.parse(
429
- fs.readFileSync(guardianTokenPath(configDir, "paired-dev-re"), "utf-8"),
430
- ) as Record<string, unknown>;
431
- expect(token.accessToken).toBe("t2");
656
+ expect(readGuardianToken("desk").accessToken).toBe("t2");
657
+ });
658
+
659
+ test("re-pairing to a new gateway drops a cached platformAssistantId", () => {
660
+ const first = pairAssistant([lockfilePath], configDir, {
661
+ credentials: credentials({ deviceId: "dev-re", token: "t1" }),
662
+ name: "desk",
663
+ });
664
+ expect(first.ok).toBe(true);
665
+ const cached = readLockfileFromDisk();
666
+ (
667
+ cached.assistants as Array<Record<string, unknown>>
668
+ )[0]!.platformAssistantId = "11111111-1111-4111-8111-111111111111";
669
+ fs.writeFileSync(lockfilePath, JSON.stringify(cached, null, 2));
670
+
671
+ const second = pairAssistant([lockfilePath], configDir, {
672
+ credentials: credentials({
673
+ deviceId: "dev-re",
674
+ token: "t2",
675
+ gatewayUrl: "https://new.example.com",
676
+ }),
677
+ name: "desk",
678
+ });
679
+
680
+ expect(second.ok).toBe(true);
681
+ const assistants = readLockfileFromDisk().assistants as Array<
682
+ Record<string, unknown>
683
+ >;
684
+ expect(assistants).toHaveLength(1);
685
+ expect(assistants[0]!.runtimeUrl).toBe("https://new.example.com");
686
+ expect(assistants[0]!).not.toHaveProperty("platformAssistantId");
432
687
  });
433
688
 
434
689
  test("deletes the just-written token when the lockfile write fails", () => {
@@ -437,12 +692,12 @@ describe("pairAssistant", () => {
437
692
  // has already been written.
438
693
  const notADir = path.join(tmpDir, "not-a-dir");
439
694
  fs.writeFileSync(notADir, "");
440
- const tokenPath = guardianTokenPath(configDir, "paired-dev-aaa");
695
+ const tokenPath = guardianTokenPath(configDir, DEFAULT_PAIRED_ID);
441
696
 
442
697
  const result = pairAssistant(
443
698
  [path.join(notADir, "lockfile.json")],
444
699
  configDir,
445
- { bundle: bundle() },
700
+ { credentials: credentials() },
446
701
  );
447
702
 
448
703
  expect(result.ok).toBe(false);
@@ -453,10 +708,10 @@ describe("pairAssistant", () => {
453
708
 
454
709
  test("restores the prior token when a re-import's lockfile write fails", () => {
455
710
  const first = pairAssistant([lockfilePath], configDir, {
456
- bundle: bundle({ deviceId: "dev-re", token: "t1" }),
711
+ credentials: credentials({ deviceId: "dev-re", token: "t1" }),
457
712
  });
458
713
  expect(first.ok).toBe(true);
459
- const tokenPath = guardianTokenPath(configDir, "paired-dev-re");
714
+ const tokenPath = guardianTokenPath(configDir, DEFAULT_PAIRED_ID);
460
715
  const priorContents = fs.readFileSync(tokenPath, "utf-8");
461
716
 
462
717
  // Reads fall back to the real lockfile (so the existing entry is found
@@ -468,16 +723,16 @@ describe("pairAssistant", () => {
468
723
  const result = pairAssistant(
469
724
  [path.join(notADir, "lockfile.json"), lockfilePath],
470
725
  configDir,
471
- { bundle: bundle({ deviceId: "dev-re", token: "t2" }) },
726
+ { credentials: credentials({ deviceId: "dev-re", token: "t2" }) },
472
727
  );
473
728
 
474
729
  expect(result.ok).toBe(false);
475
730
  expect(fs.readFileSync(tokenPath, "utf-8")).toBe(priorContents);
476
731
  });
477
732
 
478
- test("an unparseable gatewayUrl on an unvalidated bundle is refused, not thrown", () => {
733
+ test("an unparseable gatewayUrl is refused, not thrown", () => {
479
734
  const result = pairAssistant([lockfilePath], configDir, {
480
- bundle: bundle({ gatewayUrl: "not a url" }),
735
+ credentials: credentials({ gatewayUrl: "not a url" }),
481
736
  });
482
737
 
483
738
  expect(result.ok).toBe(false);
@@ -488,32 +743,1156 @@ describe("pairAssistant", () => {
488
743
  });
489
744
  });
490
745
 
491
- describe("connectImport", () => {
492
- test("registers a valid bundle and returns the wire-shaped success", () => {
493
- const result = connectImport([lockfilePath], configDir, {
494
- bundle: ` ${encodeBundle({
495
- gatewayUrl: "https://gw.example.com",
496
- token: "tok",
497
- deviceId: "dev-ci",
498
- })} `,
499
- name: "Desk Box",
746
+ describe("checkPairedAssistantName", () => {
747
+ test("mirrors pairAssistant's refusal for a colliding name", () => {
748
+ writeLockfile({
749
+ assistants: [
750
+ {
751
+ assistantId: "desk",
752
+ cloud: "local",
753
+ runtimeUrl: "http://127.0.0.1:7830",
754
+ },
755
+ ],
756
+ activeAssistant: "desk",
500
757
  });
501
758
 
502
- expect(result).toEqual({
503
- ok: true,
504
- assistantId: "desk-box",
505
- accessOnly: true,
759
+ // Same derivation as the real check: the raw name is slugified first.
760
+ const refusal = checkPairedAssistantName([lockfilePath], "Desk");
761
+ expect(refusal).not.toBeNull();
762
+ expect(refusal!.status).toBe(409);
763
+ expect(refusal!.assistantId).toBe("desk");
764
+
765
+ const real = pairAssistant([lockfilePath], configDir, {
766
+ credentials: credentials(),
767
+ name: "Desk",
506
768
  });
507
- expect(fs.existsSync(guardianTokenPath(configDir, "desk-box"))).toBe(true);
508
- const entry = (
509
- readLockfileFromDisk().assistants as Array<Record<string, unknown>>
510
- )[0]!;
511
- expect(entry).toMatchObject({
512
- assistantId: "desk-box",
513
- cloud: "paired",
514
- paired: true,
515
- runtimeUrl: "https://gw.example.com",
769
+ expect(real.ok).toBe(false);
770
+ if (real.ok) {
771
+ return;
772
+ }
773
+ expect(real.status).toBe(refusal!.status);
774
+ expect(real.error).toBe(refusal!.error);
775
+ });
776
+
777
+ test("a name with no alphanumerics is refused with a 400", () => {
778
+ const refusal = checkPairedAssistantName([lockfilePath], "///");
779
+ expect(refusal?.status).toBe(400);
780
+ });
781
+
782
+ test("a free name and an existing paired entry both pass", () => {
783
+ expect(checkPairedAssistantName([lockfilePath], "fresh")).toBeNull();
784
+
785
+ const imported = pairAssistant([lockfilePath], configDir, {
786
+ credentials: credentials(),
787
+ name: "fresh",
516
788
  });
789
+ expect(imported.ok).toBe(true);
790
+ // A re-import updates in place, so the name is still usable.
791
+ expect(checkPairedAssistantName([lockfilePath], "fresh")).toBeNull();
792
+ });
793
+ });
794
+
795
+ describe("pairingStart", () => {
796
+ test("a pairing link opens a session without a request or an approval code", async () => {
797
+ const started = await pairingStart(
798
+ "https://gw.example.com/assistant/pair#device_code=device-code-abc",
799
+ );
800
+
801
+ expect(started.ok).toBe(true);
802
+ if (!started.ok) {
803
+ return;
804
+ }
805
+ expect(fetchCalls).toHaveLength(0);
806
+ expect(started.userCode).toBeNull();
807
+ expect(started.handle).toMatch(/^[A-Za-z0-9_-]{21}$/);
808
+ expect(new Date(started.expiresAt).getTime()).toBeGreaterThan(Date.now());
809
+ // The caller never sees the device code, the device id, or anything else
810
+ // it could replay.
811
+ expect(Object.keys(started).sort()).toEqual([
812
+ "expiresAt",
813
+ "handle",
814
+ "intervalSeconds",
815
+ "ok",
816
+ "userCode",
817
+ ]);
818
+ expect(JSON.stringify(started)).not.toContain("device-code-abc");
819
+ });
820
+
821
+ test("a bare URL mints a challenge and returns the approval code", async () => {
822
+ respond = () => json(200, challengeBody());
823
+
824
+ const started = await pairingStart(" https://gw.example.com/ ");
825
+
826
+ expect(started.ok).toBe(true);
827
+ if (!started.ok) {
828
+ return;
829
+ }
830
+ expect(fetchCalls).toEqual([
831
+ {
832
+ url: "https://gw.example.com/v1/remote-web/pairing-challenge",
833
+ body: { publicBaseUrl: "https://gw.example.com" },
834
+ redirect: "error",
835
+ signal: expect.any(AbortSignal) as unknown as AbortSignal,
836
+ },
837
+ ]);
838
+ expect(started.userCode).toBe("ABCD-EFGH");
839
+ expect(started.intervalSeconds).toBe(3);
840
+ expect(JSON.stringify(started)).not.toContain("device-code-abc");
841
+ });
842
+
843
+ test("clamps the poll cadence a challenge names", async () => {
844
+ // The address is user-pasted, so the assistant answering it is untrusted:
845
+ // a caller turns this straight into a wait, and an unbounded one either
846
+ // parks the attempt forever or overflows a timer and spins.
847
+ const cadences: Array<[string, number]> = [
848
+ ["1e400", 5],
849
+ ["-1e400", 5],
850
+ ["null", 5],
851
+ ["0", 5],
852
+ ["86400", 60],
853
+ // A sub-second cadence is floored: a caller turns this straight into a
854
+ // wait, and an assistant naming one would have it poll as fast as the
855
+ // round trip allows for the code's whole TTL.
856
+ ["0.01", 1],
857
+ ["1e-9", 1],
858
+ ["3", 3],
859
+ ];
860
+ for (const [literal, expected] of cadences) {
861
+ respond = () =>
862
+ rawJson(
863
+ 200,
864
+ `{"deviceCode":"device-code-abc","userCode":"ABCD-EFGH",` +
865
+ `"expiresAt":"${new Date(Date.now() + 600_000).toISOString()}",` +
866
+ `"intervalSeconds":${literal}}`,
867
+ );
868
+
869
+ const started = await pairingStart("https://gw.example.com");
870
+
871
+ expect(started.ok).toBe(true);
872
+ if (!started.ok) {
873
+ continue;
874
+ }
875
+ expect(started.intervalSeconds).toBe(expected);
876
+ }
877
+ });
878
+
879
+ test("bounds the session by the code TTL, not by the instant the assistant reports", async () => {
880
+ respond = () =>
881
+ json(
882
+ 200,
883
+ challengeBody({
884
+ expiresAt: new Date(Date.now() + 400 * 86_400_000).toISOString(),
885
+ }),
886
+ );
887
+
888
+ const started = await pairingStart("https://gw.example.com");
889
+
890
+ expect(started.ok).toBe(true);
891
+ if (!started.ok) {
892
+ return;
893
+ }
894
+ // A remote naming a further instant cannot buy itself a longer session.
895
+ expect(new Date(started.expiresAt).getTime()).toBeLessThanOrEqual(
896
+ Date.now() + 10 * 60_000,
897
+ );
898
+ expect(new Date(started.expiresAt).getTime()).toBeGreaterThan(Date.now());
899
+ });
900
+
901
+ test("a challenge response missing its codes is a gateway failure", async () => {
902
+ respond = () => json(200, { verificationUri: "https://gw.example.com" });
903
+
904
+ const started = await pairingStart("https://gw.example.com");
905
+
906
+ expect(started.ok).toBe(false);
907
+ if (started.ok) {
908
+ return;
909
+ }
910
+ expect(started.reason).toBe("gateway");
911
+ expect(started.status).toBe(502);
912
+ });
913
+
914
+ test("a refused challenge is retryable and carries the status", async () => {
915
+ respond = () => json(429, { error: { code: "RATE_LIMITED" } });
916
+
917
+ const started = await pairingStart("https://gw.example.com");
918
+
919
+ expect(started.ok).toBe(false);
920
+ if (started.ok) {
921
+ return;
922
+ }
923
+ // Nothing was minted, so the same address is worth another attempt.
924
+ expect(started.reason).toBe("gateway-retryable");
925
+ expect(started.error).toContain("429");
926
+ });
927
+
928
+ test("an unreachable assistant is reported as a transport failure", async () => {
929
+ respond = () => {
930
+ throw new TypeError("connection refused");
931
+ };
932
+
933
+ const started = await pairingStart("https://gw.example.com");
934
+
935
+ expect(started.ok).toBe(false);
936
+ if (started.ok) {
937
+ return;
938
+ }
939
+ expect(started.reason).toBe("unreachable");
940
+ expect(started.status).toBe(503);
941
+ });
942
+
943
+ test("refuses to follow a redirect off the address that was checked", async () => {
944
+ // Without `redirect: "error"` this 307 would carry the POST to a loopback
945
+ // service, walking straight past the address checks.
946
+ respond = () =>
947
+ redirectTo("http://127.0.0.1:7830/v1/remote-web/pairing-challenge");
948
+
949
+ const started = await pairingStart("https://gw.example.com");
950
+
951
+ expect(started.ok).toBe(false);
952
+ if (started.ok) {
953
+ return;
954
+ }
955
+ expect(started.reason).toBe("unreachable");
956
+ expect(started.status).toBe(503);
957
+ expect(started.error).toContain("Could not reach that assistant");
958
+ // One request, to the checked address, and it asked not to be redirected.
959
+ expect(fetchCalls).toHaveLength(1);
960
+ expect(fetchCalls[0]!.url).toBe(
961
+ "https://gw.example.com/v1/remote-web/pairing-challenge",
962
+ );
963
+ expect(fetchCalls[0]!.redirect).toBe("error");
964
+ });
965
+
966
+ test("an over-cap challenge body is refused instead of buffered", async () => {
967
+ let pulls = 0;
968
+ respond = () =>
969
+ streamedBody(1024, () => {
970
+ pulls += 1;
971
+ });
972
+
973
+ const started = await pairingStart("https://gw.example.com");
974
+
975
+ expect(started.ok).toBe(false);
976
+ if (started.ok) {
977
+ return;
978
+ }
979
+ expect(started.reason).toBe("gateway");
980
+ expect(started.status).toBe(502);
981
+ expect(started.error).toContain("too large");
982
+ // The read stopped just past the 64 KiB cap rather than draining 8 MiB.
983
+ expect(pulls).toBeLessThan(20);
984
+ });
985
+
986
+ test("rejects addresses that can never reach a remote assistant", async () => {
987
+ const cases: Array<[string, string]> = [
988
+ ["http://localhost:7830", "loopback"],
989
+ ["https://127.0.0.1", "loopback"],
990
+ // The reserved `.localhost` namespace resolves to loopback, and a
991
+ // trailing DNS root dot names the same host.
992
+ ["https://foo.localhost", "loopback"],
993
+ ["https://localhost.", "loopback"],
994
+ ["https://10.0.0.1", "private-address"],
995
+ ["https://192.168.1.5", "private-address"],
996
+ // The cloud instance metadata endpoint, the classic blind-SSRF target.
997
+ ["https://169.254.169.254", "private-address"],
998
+ ["https://[fd00::1]", "private-address"],
999
+ ["https://[::ffff:10.0.0.1]", "private-address"],
1000
+ ["http://gw.example.com", "non-https"],
1001
+ ["https://login.tailscale.com/admin/invite/abc", "service-website"],
1002
+ ["not a url", "unparseable"],
1003
+ ];
1004
+
1005
+ for (const [address, rejection] of cases) {
1006
+ const started = await pairingStart(address);
1007
+ expect(started.ok).toBe(false);
1008
+ if (started.ok) {
1009
+ continue;
1010
+ }
1011
+ expect(started.reason).toBe("invalid-address");
1012
+ expect(started.status).toBe(400);
1013
+ expect(started.rejection).toBe(
1014
+ rejection as NonNullable<typeof started.rejection>,
1015
+ );
1016
+ }
1017
+ // A refused address never reaches the network.
1018
+ expect(fetchCalls).toHaveLength(0);
1019
+ });
1020
+
1021
+ test("names the tunnel vendor when the address is its own website", async () => {
1022
+ const started = await pairingStart("https://login.tailscale.com/admin");
1023
+
1024
+ expect(started.ok).toBe(false);
1025
+ if (started.ok) {
1026
+ return;
1027
+ }
1028
+ expect(started.error).toContain("Tailscale");
1029
+ });
1030
+
1031
+ test("a missing or oversized address is refused before parsing", async () => {
1032
+ for (const address of [undefined, "", " ", 42]) {
1033
+ const started = await pairingStart(address);
1034
+ expect(started.ok).toBe(false);
1035
+ if (started.ok) {
1036
+ continue;
1037
+ }
1038
+ expect(started.reason).toBe("invalid-address");
1039
+ }
1040
+
1041
+ const oversized = `https://gw.example.com/${"a".repeat(2 * 1024)}`;
1042
+ const started = await pairingStart(oversized);
1043
+ expect(started.ok).toBe(false);
1044
+ if (started.ok) {
1045
+ return;
1046
+ }
1047
+ expect(started.error).toContain("too long");
1048
+ expect(fetchCalls).toHaveLength(0);
1049
+ });
1050
+ });
1051
+
1052
+ describe("loopback refusals agree across the pairing path", () => {
1053
+ // One predicate backs both entry points, so an address class can never be
1054
+ // refused by the one the user pasted into and accepted by the one an import
1055
+ // goes through.
1056
+ test.each([
1057
+ ["https://localhost", "bare localhost"],
1058
+ ["https://foo.localhost", "reserved .localhost namespace"],
1059
+ ["https://a.b.localhost.", "absolute .localhost namespace"],
1060
+ ["https://localhost.", "absolute localhost name"],
1061
+ ["https://127.0.0.1.", "absolute IPv4 loopback literal"],
1062
+ ["https://[::1]", "IPv6 loopback"],
1063
+ ["https://0.0.0.0", "IPv4 wildcard bind"],
1064
+ ])("refuses %s (%s) at both entry points", async (address) => {
1065
+ const started = await pairingStart(address);
1066
+ expect(started.ok).toBe(false);
1067
+ if (started.ok) {
1068
+ return;
1069
+ }
1070
+ expect(started.reason).toBe("invalid-address");
1071
+ expect(started.rejection).toBe("loopback");
1072
+
1073
+ const imported = pairAssistant([lockfilePath], configDir, {
1074
+ credentials: credentials({ gatewayUrl: address }),
1075
+ });
1076
+ expect(imported.ok).toBe(false);
1077
+ if (imported.ok) {
1078
+ return;
1079
+ }
1080
+ expect(imported.status).toBe(422);
1081
+ expect(imported.error).toContain("non-loopback");
1082
+ });
1083
+ });
1084
+
1085
+ describe("pairingPoll", () => {
1086
+ const startFromLink = async (
1087
+ address = "https://gw.example.com/assistant/pair#device_code=device-code-abc",
1088
+ ): Promise<string> => {
1089
+ const started = await pairingStart(address);
1090
+ if (!started.ok) {
1091
+ throw new Error(`unexpected start failure: ${started.error}`);
1092
+ }
1093
+ return started.handle;
1094
+ };
1095
+
1096
+ test("a pre-approved link imports on the first poll", async () => {
1097
+ respond = () => json(200, approvedBody());
1098
+ const handle = await startFromLink();
1099
+
1100
+ const result = await pairingPoll([lockfilePath], configDir, { handle });
1101
+
1102
+ expect(result).toEqual({
1103
+ ok: true,
1104
+ status: "imported",
1105
+ assistantId: expect.stringMatching(/^paired-/) as unknown as string,
1106
+ updated: false,
1107
+ accessOnly: false,
1108
+ });
1109
+ expect(fetchCalls).toHaveLength(1);
1110
+ expect(fetchCalls[0]!.url).toBe(
1111
+ "https://gw.example.com/v1/remote-web/pairing-token",
1112
+ );
1113
+ expect(fetchCalls[0]!.body.deviceCode).toBe("device-code-abc");
1114
+ expect(fetchCalls[0]!.body.platform).toBe("desktop");
1115
+ expect(fetchCalls[0]!.body.deviceId).toMatch(/^[A-Za-z0-9_-]{21}$/);
1116
+
1117
+ const entry = (
1118
+ readLockfileFromDisk().assistants as Array<Record<string, unknown>>
1119
+ )[0]!;
1120
+ expect(entry).toMatchObject({
1121
+ cloud: "paired",
1122
+ paired: true,
1123
+ // The pair-page path is stripped back to the public base.
1124
+ runtimeUrl: "https://gw.example.com",
1125
+ });
1126
+ const token = readGuardianToken(entry.assistantId as string);
1127
+ expect(token.accessToken).toBe("acc-tok");
1128
+ expect(token.refreshToken).toBe("refresh-tok");
1129
+ expect(token.deviceId).toBe(fetchCalls[0]!.body.deviceId);
1130
+ });
1131
+
1132
+ test("a gateway that returns no body refresh token imports access-only", async () => {
1133
+ respond = () =>
1134
+ json(200, {
1135
+ ...approvedBody(),
1136
+ refreshToken: undefined,
1137
+ refreshTokenExpiresAt: undefined,
1138
+ });
1139
+ const handle = await startFromLink();
1140
+
1141
+ const result = await pairingPoll([lockfilePath], configDir, {
1142
+ handle,
1143
+ name: "Desk Box",
1144
+ });
1145
+
1146
+ expect(result).toEqual({
1147
+ ok: true,
1148
+ status: "imported",
1149
+ assistantId: "desk-box",
1150
+ updated: false,
1151
+ accessOnly: true,
1152
+ });
1153
+ expect(readGuardianToken("desk-box").refreshToken).toBe("");
1154
+ });
1155
+
1156
+ test("stays pending until the host approves, adopting the gateway's cadence", async () => {
1157
+ respond = () => json(202, pendingBody());
1158
+ const handle = await startFromLink();
1159
+
1160
+ const pending = await pairingPoll([lockfilePath], configDir, { handle });
1161
+ expect(pending).toEqual({
1162
+ ok: true,
1163
+ status: "pending",
1164
+ expiresAt: expect.any(String) as unknown as string,
1165
+ intervalSeconds: 7,
1166
+ });
1167
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1168
+
1169
+ respond = () => json(200, approvedBody());
1170
+ const imported = await pairingPoll([lockfilePath], configDir, { handle });
1171
+ expect(imported.ok).toBe(true);
1172
+ if (!imported.ok) {
1173
+ return;
1174
+ }
1175
+ expect(imported.status).toBe("imported");
1176
+ });
1177
+
1178
+ test("a 202 may shorten the deadline but never extend it", async () => {
1179
+ const started = await pairingStart(
1180
+ "https://gw.example.com/assistant/pair#device_code=device-code-abc",
1181
+ );
1182
+ expect(started.ok).toBe(true);
1183
+ if (!started.ok) {
1184
+ return;
1185
+ }
1186
+ const openedAtMs = new Date(started.expiresAt).getTime();
1187
+
1188
+ // A remote that keeps naming a fresh expiry would otherwise hold the
1189
+ // session open on its own TTL rather than the code's.
1190
+ respond = () =>
1191
+ json(
1192
+ 202,
1193
+ pendingBody({
1194
+ expiresAt: new Date(Date.now() + 365 * 86_400_000).toISOString(),
1195
+ }),
1196
+ );
1197
+ const extended = await pairingPoll([lockfilePath], configDir, {
1198
+ handle: started.handle,
1199
+ });
1200
+ expect(extended.ok).toBe(true);
1201
+ if (!extended.ok || extended.status !== "pending") {
1202
+ return;
1203
+ }
1204
+ expect(new Date(extended.expiresAt).getTime()).toBe(openedAtMs);
1205
+
1206
+ // Shortening is the gateway's to do: the code may be revoked early.
1207
+ const sooner = new Date(Date.now() + 30_000).toISOString();
1208
+ respond = () => json(202, pendingBody({ expiresAt: sooner }));
1209
+ const shortened = await pairingPoll([lockfilePath], configDir, {
1210
+ handle: started.handle,
1211
+ });
1212
+ expect(shortened.ok).toBe(true);
1213
+ if (!shortened.ok || shortened.status !== "pending") {
1214
+ return;
1215
+ }
1216
+ expect(shortened.expiresAt).toBe(sooner);
1217
+ });
1218
+
1219
+ test("clamps the poll cadence a pending reply names", async () => {
1220
+ for (const [literal, expected] of [
1221
+ ["1e400", 5],
1222
+ ["86400", 60],
1223
+ // Floored, for the same reason a challenge's cadence is.
1224
+ ["0.01", 1],
1225
+ ["7", 7],
1226
+ ] as Array<[string, number]>) {
1227
+ respond = () =>
1228
+ rawJson(
1229
+ 202,
1230
+ `{"status":"pending",` +
1231
+ `"expiresAt":"${new Date(Date.now() + 300_000).toISOString()}",` +
1232
+ `"intervalSeconds":${literal}}`,
1233
+ );
1234
+ const handle = await startFromLink();
1235
+
1236
+ const pending = await pairingPoll([lockfilePath], configDir, { handle });
1237
+
1238
+ expect(pending.ok).toBe(true);
1239
+ if (!pending.ok || pending.status !== "pending") {
1240
+ continue;
1241
+ }
1242
+ expect(pending.intervalSeconds).toBe(expected);
1243
+ }
1244
+ });
1245
+
1246
+ test("a colliding name is refused before the one-time code is spent", async () => {
1247
+ writeLockfile({
1248
+ assistants: [
1249
+ {
1250
+ assistantId: "desk",
1251
+ cloud: "local",
1252
+ runtimeUrl: "http://127.0.0.1:7830",
1253
+ },
1254
+ ],
1255
+ activeAssistant: "desk",
1256
+ });
1257
+ respond = () => json(200, approvedBody());
1258
+ const handle = await startFromLink();
1259
+
1260
+ const refused = await pairingPoll([lockfilePath], configDir, {
1261
+ handle,
1262
+ name: "desk",
1263
+ });
1264
+
1265
+ expect(refused.ok).toBe(false);
1266
+ if (refused.ok) {
1267
+ return;
1268
+ }
1269
+ // The pre-check reason, not the post-exchange one: it is what tells a
1270
+ // caller holding the handle that the session is still worth keeping.
1271
+ expect(refused.reason).toBe("import-precheck");
1272
+ expect(pairingSessionSurvives(refused.reason)).toBe(true);
1273
+ expect(refused.status).toBe(409);
1274
+ // The exchange never went out, so the code is still exchangeable and the
1275
+ // user does not need a fresh pairing link.
1276
+ expect(fetchCalls).toHaveLength(0);
1277
+
1278
+ // The very same session completes once the caller picks a free name.
1279
+ const imported = await pairingPoll([lockfilePath], configDir, {
1280
+ handle,
1281
+ name: "spare",
1282
+ });
1283
+ expect(imported.ok).toBe(true);
1284
+ if (!imported.ok || imported.status !== "imported") {
1285
+ return;
1286
+ }
1287
+ expect(imported.assistantId).toBe("spare");
1288
+ expect(fetchCalls).toHaveLength(1);
1289
+ });
1290
+
1291
+ test("re-pairing the same assistant with no name updates its entry", async () => {
1292
+ // Each attempt mints its own device id, so a default id keyed on that
1293
+ // would register a second entry (and a second guardian token) for the
1294
+ // assistant the user was told to re-pair.
1295
+ respond = () => json(200, approvedBody({ accessToken: "acc-1" }));
1296
+ const first = await pairingPoll([lockfilePath], configDir, {
1297
+ handle: await startFromLink(),
1298
+ });
1299
+ expect(first.ok).toBe(true);
1300
+ if (!first.ok || first.status !== "imported") {
1301
+ return;
1302
+ }
1303
+ expect(first.updated).toBe(false);
1304
+
1305
+ respond = () => json(200, approvedBody({ accessToken: "acc-2" }));
1306
+ const second = await pairingPoll([lockfilePath], configDir, {
1307
+ handle: await startFromLink(),
1308
+ });
1309
+
1310
+ expect(second.ok).toBe(true);
1311
+ if (!second.ok || second.status !== "imported") {
1312
+ return;
1313
+ }
1314
+ expect(second.assistantId).toBe(first.assistantId);
1315
+ expect(second.updated).toBe(true);
1316
+ expect(readLockfileFromDisk().assistants).toHaveLength(1);
1317
+ expect(fs.readdirSync(path.join(configDir, "assistants"))).toEqual([
1318
+ first.assistantId,
1319
+ ]);
1320
+ expect(readGuardianToken(first.assistantId).accessToken).toBe("acc-2");
1321
+ });
1322
+
1323
+ test("records the platform the caller names, defaulting to desktop", async () => {
1324
+ respond = () => json(202, pendingBody());
1325
+
1326
+ const cliHandle = await startFromLink();
1327
+ await pairingPoll([lockfilePath], configDir, {
1328
+ handle: cliHandle,
1329
+ platform: "cli",
1330
+ });
1331
+ expect(fetchCalls[0]!.body.platform).toBe("cli");
1332
+
1333
+ const unknownHandle = await startFromLink();
1334
+ await pairingPoll([lockfilePath], configDir, {
1335
+ handle: unknownHandle,
1336
+ platform: "toaster",
1337
+ });
1338
+ expect(fetchCalls[1]!.body.platform).toBe("desktop");
1339
+ });
1340
+
1341
+ test("an expired challenge is refused without spending a request", async () => {
1342
+ respond = () =>
1343
+ json(
1344
+ 200,
1345
+ challengeBody({
1346
+ expiresAt: new Date(Date.now() - 1_000).toISOString(),
1347
+ }),
1348
+ );
1349
+ const started = await pairingStart("https://gw.example.com");
1350
+ expect(started.ok).toBe(true);
1351
+ if (!started.ok) {
1352
+ return;
1353
+ }
1354
+
1355
+ const result = await pairingPoll([lockfilePath], configDir, {
1356
+ handle: started.handle,
1357
+ });
1358
+
1359
+ expect(result.ok).toBe(false);
1360
+ if (result.ok) {
1361
+ return;
1362
+ }
1363
+ expect(result.reason).toBe("expired");
1364
+ expect(result.status).toBe(410);
1365
+ // Only the challenge mint went out; the exchange was never attempted.
1366
+ expect(fetchCalls).toHaveLength(1);
1367
+ });
1368
+
1369
+ test("a denied or expired code ends the session", async () => {
1370
+ respond = () =>
1371
+ json(401, { error: { code: "INVALID_OR_EXPIRED_DEVICE_CODE" } });
1372
+ const handle = await startFromLink();
1373
+
1374
+ const denied = await pairingPoll([lockfilePath], configDir, { handle });
1375
+ expect(denied.ok).toBe(false);
1376
+ if (denied.ok) {
1377
+ return;
1378
+ }
1379
+ expect(denied.reason).toBe("expired");
1380
+
1381
+ // The session is gone, so a further poll cannot replay the spent code.
1382
+ const again = await pairingPoll([lockfilePath], configDir, { handle });
1383
+ expect(again.ok).toBe(false);
1384
+ if (again.ok) {
1385
+ return;
1386
+ }
1387
+ expect(again.reason).toBe("unknown-session");
1388
+ expect(fetchCalls).toHaveLength(1);
1389
+ });
1390
+
1391
+ test("a transport failure leaves the session pollable", async () => {
1392
+ respond = () => {
1393
+ throw new TypeError("connection reset");
1394
+ };
1395
+ const handle = await startFromLink();
1396
+
1397
+ const failed = await pairingPoll([lockfilePath], configDir, { handle });
1398
+ expect(failed.ok).toBe(false);
1399
+ if (failed.ok) {
1400
+ return;
1401
+ }
1402
+ expect(failed.reason).toBe("unreachable");
1403
+
1404
+ respond = () => json(200, approvedBody());
1405
+ const imported = await pairingPoll([lockfilePath], configDir, { handle });
1406
+ expect(imported.ok).toBe(true);
1407
+ });
1408
+
1409
+ test("a repairable gateway refusal stays retryable and pollable", async () => {
1410
+ respond = () => json(503, { error: { code: "GUARDIAN_REPAIR_REQUIRED" } });
1411
+ const handle = await startFromLink();
1412
+
1413
+ const refused = await pairingPoll([lockfilePath], configDir, { handle });
1414
+
1415
+ expect(refused.ok).toBe(false);
1416
+ if (refused.ok) {
1417
+ return;
1418
+ }
1419
+ // The gateway releases the challenge on a repairable failure, so this is
1420
+ // the retryable class rather than a settled rejection.
1421
+ expect(refused.reason).toBe("gateway-retryable");
1422
+ expect(refused.status).toBe(502);
1423
+ expect(refused.error).toContain("503");
1424
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1425
+
1426
+ // The same session exchanges the still-live code once the gateway is
1427
+ // repaired; the user never has to mint and approve another one.
1428
+ respond = () => json(200, approvedBody());
1429
+ const imported = await pairingPoll([lockfilePath], configDir, { handle });
1430
+ expect(imported.ok).toBe(true);
1431
+ if (!imported.ok) {
1432
+ return;
1433
+ }
1434
+ expect(imported.status).toBe("imported");
1435
+ expect(fetchCalls).toHaveLength(2);
1436
+ });
1437
+
1438
+ test("an unusable approval body is a gateway failure", async () => {
1439
+ respond = () => json(200, { status: "approved" });
1440
+ const handle = await startFromLink();
1441
+
1442
+ const result = await pairingPoll([lockfilePath], configDir, { handle });
1443
+
1444
+ expect(result.ok).toBe(false);
1445
+ if (result.ok) {
1446
+ return;
1447
+ }
1448
+ expect(result.reason).toBe("gateway");
1449
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1450
+ // The gateway spent the code before replying, so the session is dead.
1451
+ expect(pairingCancel(handle)).toBe(false);
1452
+ });
1453
+
1454
+ test.each([
1455
+ ["an empty accessToken", { accessToken: "" }],
1456
+ ["a non-string accessToken", { accessToken: 42 }],
1457
+ ["a non-string refreshToken", { refreshToken: 7 }],
1458
+ ["an object refreshTokenExpiresAt", { refreshTokenExpiresAt: {} }],
1459
+ ["a non-string refreshAfter", { refreshAfter: 900 }],
1460
+ // Refresh fields are all-or-none: a partial set would persist a zero
1461
+ // expiry while reporting the pairing as renewable.
1462
+ ["a refresh token but no expiry", { refreshTokenExpiresAt: undefined }],
1463
+ ["an expiry but no refresh token", { refreshToken: undefined }],
1464
+ ["an expiry but an empty refresh token", { refreshToken: "" }],
1465
+ ["a zero refreshTokenExpiresAt", { refreshTokenExpiresAt: 0 }],
1466
+ ["a negative refreshTokenExpiresAt", { refreshTokenExpiresAt: -1 }],
1467
+ ["an unparseable refreshTokenExpiresAt", { refreshTokenExpiresAt: "soon" }],
1468
+ ])(
1469
+ "a reply with %s is a gateway failure, not a half-written credential",
1470
+ async (_label, overrides) => {
1471
+ respond = () => json(200, approvedBody(overrides));
1472
+ const handle = await startFromLink();
1473
+
1474
+ const result = await pairingPoll([lockfilePath], configDir, { handle });
1475
+
1476
+ expect(result.ok).toBe(false);
1477
+ if (result.ok) {
1478
+ return;
1479
+ }
1480
+ expect(result.reason).toBe("gateway");
1481
+ expect(result.status).toBe(502);
1482
+ // Nothing was persisted: no lockfile entry and no guardian token.
1483
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1484
+ expect(fs.existsSync(path.join(configDir, "assistants"))).toBe(false);
1485
+ // The gateway spent the code before replying, so the session is dead.
1486
+ expect(pairingCancel(handle)).toBe(false);
1487
+ },
1488
+ );
1489
+
1490
+ test("a reply with no refresh fields at all still imports access-only", async () => {
1491
+ respond = () =>
1492
+ json(200, {
1493
+ status: "approved",
1494
+ accessToken: "acc-tok",
1495
+ accessTokenExpiresAt: new Date(Date.now() + 3_600_000).toISOString(),
1496
+ guardianId: "guardian-1",
1497
+ assistantId: "self",
1498
+ });
1499
+ const handle = await startFromLink();
1500
+
1501
+ const result = await pairingPoll([lockfilePath], configDir, {
1502
+ handle,
1503
+ name: "Old Gateway",
1504
+ });
1505
+
1506
+ expect(result).toEqual({
1507
+ ok: true,
1508
+ status: "imported",
1509
+ assistantId: "old-gateway",
1510
+ updated: false,
1511
+ accessOnly: true,
1512
+ });
1513
+ const token = readGuardianToken("old-gateway");
1514
+ expect(token.accessToken).toBe("acc-tok");
1515
+ expect(token.refreshToken).toBe("");
1516
+ expect(token.refreshAfter).toBe("");
1517
+ expect(token.refreshTokenExpiresAt).toBe(0);
1518
+ });
1519
+
1520
+ test("a numeric refreshTokenExpiresAt is kept rather than dropped", async () => {
1521
+ const expiresAtMs = Date.now() + 86_400_000;
1522
+ respond = () =>
1523
+ json(200, approvedBody({ refreshTokenExpiresAt: expiresAtMs }));
1524
+ const handle = await startFromLink();
1525
+
1526
+ const result = await pairingPoll([lockfilePath], configDir, {
1527
+ handle,
1528
+ name: "Numeric Expiry",
1529
+ });
1530
+
1531
+ expect(result.ok).toBe(true);
1532
+ expect(readGuardianToken("numeric-expiry").refreshTokenExpiresAt).toBe(
1533
+ expiresAtMs,
1534
+ );
1535
+ });
1536
+
1537
+ test("a local write that fails after the exchange surfaces as an import failure", async () => {
1538
+ // A lockfile path whose parent is a regular file makes the write fail
1539
+ // after the code has already been spent. That is the `import` reason: the
1540
+ // session is gone and a retry needs a fresh code, which is exactly what
1541
+ // the pre-check reason does NOT mean.
1542
+ const notADir = path.join(tmpDir, "not-a-dir");
1543
+ fs.writeFileSync(notADir, "");
1544
+ respond = () => json(200, approvedBody());
1545
+ const handle = await startFromLink();
1546
+
1547
+ const result = await pairingPoll(
1548
+ [path.join(notADir, "lockfile.json")],
1549
+ configDir,
1550
+ { handle },
1551
+ );
1552
+
1553
+ expect(result.ok).toBe(false);
1554
+ if (result.ok) {
1555
+ return;
1556
+ }
1557
+ expect(result.reason).toBe("import");
1558
+ expect(pairingSessionSurvives(result.reason)).toBe(false);
1559
+ // The exchange did go out, so the code is spent and the session is gone.
1560
+ expect(fetchCalls).toHaveLength(1);
1561
+ const again = await pairingPoll([lockfilePath], configDir, { handle });
1562
+ expect(again.ok).toBe(false);
1563
+ if (again.ok) {
1564
+ return;
1565
+ }
1566
+ expect(again.reason).toBe("unknown-session");
1567
+ });
1568
+
1569
+ test("a redirected token exchange is refused and stays pollable", async () => {
1570
+ respond = () => redirectTo("http://169.254.169.254/latest/meta-data/");
1571
+ const handle = await startFromLink();
1572
+
1573
+ const redirected = await pairingPoll([lockfilePath], configDir, { handle });
1574
+
1575
+ expect(redirected.ok).toBe(false);
1576
+ if (redirected.ok) {
1577
+ return;
1578
+ }
1579
+ expect(redirected.reason).toBe("unreachable");
1580
+ expect(fetchCalls).toHaveLength(1);
1581
+ expect(fetchCalls[0]!.url).toBe(
1582
+ "https://gw.example.com/v1/remote-web/pairing-token",
1583
+ );
1584
+ expect(fetchCalls[0]!.redirect).toBe("error");
1585
+
1586
+ respond = () => json(200, approvedBody());
1587
+ const retried = await pairingPoll([lockfilePath], configDir, { handle });
1588
+ expect(retried.ok).toBe(true);
1589
+ });
1590
+
1591
+ test("an over-cap approval body is a gateway failure", async () => {
1592
+ let pulls = 0;
1593
+ respond = () =>
1594
+ streamedBody(1024, () => {
1595
+ pulls += 1;
1596
+ });
1597
+ const handle = await startFromLink();
1598
+
1599
+ const result = await pairingPoll([lockfilePath], configDir, { handle });
1600
+
1601
+ expect(result.ok).toBe(false);
1602
+ if (result.ok) {
1603
+ return;
1604
+ }
1605
+ expect(result.reason).toBe("gateway");
1606
+ expect(result.error).toContain("too large");
1607
+ expect(pulls).toBeLessThan(20);
1608
+ // Nothing was imported from a body that was never parsed.
1609
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1610
+ });
1611
+
1612
+ test("an unknown handle is refused without a request", async () => {
1613
+ for (const handle of ["nope", "", undefined, 42]) {
1614
+ const result = await pairingPoll([lockfilePath], configDir, { handle });
1615
+ expect(result.ok).toBe(false);
1616
+ if (result.ok) {
1617
+ continue;
1618
+ }
1619
+ expect(result.reason).toBe("unknown-session");
1620
+ expect(result.status).toBe(404);
1621
+ }
1622
+ expect(fetchCalls).toHaveLength(0);
1623
+ });
1624
+ });
1625
+
1626
+ describe("pairingCancel", () => {
1627
+ test("drops a live session so it can no longer be polled", async () => {
1628
+ const started = await pairingStart(
1629
+ "https://gw.example.com/assistant/pair#device_code=device-code-abc",
1630
+ );
1631
+ expect(started.ok).toBe(true);
1632
+ if (!started.ok) {
1633
+ return;
1634
+ }
1635
+
1636
+ expect(pairingCancel(started.handle)).toBe(true);
1637
+ // A second cancel is a no-op, and the handle is dead.
1638
+ expect(pairingCancel(started.handle)).toBe(false);
1639
+
1640
+ const result = await pairingPoll([lockfilePath], configDir, {
1641
+ handle: started.handle,
1642
+ });
1643
+ expect(result.ok).toBe(false);
1644
+ if (result.ok) {
1645
+ return;
1646
+ }
1647
+ expect(result.reason).toBe("unknown-session");
1648
+ expect(fetchCalls).toHaveLength(0);
1649
+ });
1650
+
1651
+ /**
1652
+ * Hold the token exchange open until the returned release runs, so a cancel
1653
+ * can land in the same window a dismissed dialog's cancel lands in.
1654
+ */
1655
+ const heldApproval = (): (() => void) => {
1656
+ let release = (): void => {};
1657
+ const held = new Promise<void>((resolve) => {
1658
+ release = resolve;
1659
+ });
1660
+ respond = async () => {
1661
+ await held;
1662
+ return json(200, approvedBody());
1663
+ };
1664
+ return () => release();
1665
+ };
1666
+
1667
+ const startLinkSession = async (): Promise<string> => {
1668
+ const started = await pairingStart(
1669
+ "https://gw.example.com/assistant/pair#device_code=device-code-abc",
1670
+ );
1671
+ if (!started.ok) {
1672
+ throw new Error(`unexpected start failure: ${started.error}`);
1673
+ }
1674
+ return started.handle;
1675
+ };
1676
+
1677
+ test("a cancel while the exchange is in flight persists nothing", async () => {
1678
+ const approve = heldApproval();
1679
+ const handle = await startLinkSession();
1680
+ const polled = pairingPoll([lockfilePath], configDir, { handle });
1681
+ await requestsInFlight(1);
1682
+
1683
+ expect(pairingCancel(handle)).toBe(true);
1684
+ // The gateway approves anyway, after the caller walked away.
1685
+ approve();
1686
+ const result = await polled;
1687
+
1688
+ expect(result.ok).toBe(false);
1689
+ if (result.ok) {
1690
+ return;
1691
+ }
1692
+ expect(result.reason).toBe("unknown-session");
1693
+ expect(result.status).toBe(404);
1694
+ // No lockfile entry and no guardian token for a pairing the user dropped.
1695
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1696
+ expect(fs.existsSync(path.join(configDir, "assistants"))).toBe(false);
1697
+ });
1698
+
1699
+ test("an aborted exchange is a dead session, not a retryable transport failure", async () => {
1700
+ respond = (call) =>
1701
+ new Promise<Response>((_resolve, reject) => {
1702
+ call.signal?.addEventListener("abort", () => {
1703
+ reject(new Error("aborted"));
1704
+ });
1705
+ });
1706
+ const handle = await startLinkSession();
1707
+ const polled = pairingPoll([lockfilePath], configDir, { handle });
1708
+ await requestsInFlight(1);
1709
+
1710
+ expect(pairingCancel(handle)).toBe(true);
1711
+ const result = await polled;
1712
+
1713
+ expect(result.ok).toBe(false);
1714
+ if (result.ok) {
1715
+ return;
1716
+ }
1717
+ // `unreachable` is the retryable class, so a session the caller killed
1718
+ // must never be reported as one.
1719
+ expect(result.reason).toBe("unknown-session");
1720
+ expect(fetchCalls[0]!.signal?.aborted).toBe(true);
1721
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1722
+ expect(fs.existsSync(path.join(configDir, "assistants"))).toBe(false);
1723
+ });
1724
+
1725
+ test("an exchange nobody cancels still imports", async () => {
1726
+ const approve = heldApproval();
1727
+ const handle = await startLinkSession();
1728
+ const polled = pairingPoll([lockfilePath], configDir, { handle });
1729
+ await requestsInFlight(1);
1730
+
1731
+ approve();
1732
+ const result = await polled;
1733
+
1734
+ expect(result.ok).toBe(true);
1735
+ if (!result.ok) {
1736
+ return;
1737
+ }
1738
+ expect(result.status).toBe("imported");
1739
+ const entry = (
1740
+ readLockfileFromDisk().assistants as Array<Record<string, unknown>>
1741
+ )[0]!;
1742
+ expect(entry).toMatchObject({ cloud: "paired", paired: true });
1743
+ expect(readGuardianToken(entry.assistantId as string).accessToken).toBe(
1744
+ "acc-tok",
1745
+ );
1746
+ });
1747
+
1748
+ test("a non-string handle is refused rather than throwing", () => {
1749
+ expect(pairingCancel(undefined)).toBe(false);
1750
+ expect(pairingCancel(7)).toBe(false);
1751
+ });
1752
+
1753
+ test("a session the expiry sweep evicts mid-exchange persists nothing", async () => {
1754
+ let release = (): void => {};
1755
+ const held = new Promise<void>((resolve) => {
1756
+ release = resolve;
1757
+ });
1758
+ respond = async (call) => {
1759
+ if (call.url.includes("pairing-challenge")) {
1760
+ return json(
1761
+ 200,
1762
+ challengeBody({
1763
+ expiresAt: new Date(Date.now() + 200).toISOString(),
1764
+ }),
1765
+ );
1766
+ }
1767
+ await held;
1768
+ return json(200, approvedBody());
1769
+ };
1770
+ const started = await pairingStart("https://gw.example.com");
1771
+ expect(started.ok).toBe(true);
1772
+ if (!started.ok) {
1773
+ return;
1774
+ }
1775
+
1776
+ const polled = pairingPoll([lockfilePath], configDir, {
1777
+ handle: started.handle,
1778
+ });
1779
+ await requestsInFlight(2);
1780
+ // The challenge's TTL runs out with the exchange still open, then any
1781
+ // pairing entry point sweeps the session out of the map.
1782
+ await sleep(300);
1783
+ expect(pairingCancel("no-such-handle")).toBe(false);
1784
+
1785
+ // The gateway approves after the sweep, for a session nobody holds.
1786
+ release();
1787
+ const result = await polled;
1788
+
1789
+ expect(result.ok).toBe(false);
1790
+ if (result.ok) {
1791
+ return;
1792
+ }
1793
+ expect(result.reason).toBe("unknown-session");
1794
+ expect(result.status).toBe(404);
1795
+ expect(fs.existsSync(lockfilePath)).toBe(false);
1796
+ expect(fs.existsSync(path.join(configDir, "assistants"))).toBe(false);
1797
+ });
1798
+ });
1799
+
1800
+ describe("connectImport", () => {
1801
+ test("registers a valid bundle and returns the wire-shaped success", () => {
1802
+ const result = connectImport([lockfilePath], configDir, {
1803
+ bundle: ` ${encodeBundle({
1804
+ gatewayUrl: "https://gw.example.com",
1805
+ token: "tok",
1806
+ deviceId: "dev-ci",
1807
+ })} `,
1808
+ name: "Desk Box",
1809
+ });
1810
+
1811
+ expect(result).toEqual({
1812
+ ok: true,
1813
+ assistantId: "desk-box",
1814
+ updated: false,
1815
+ accessOnly: true,
1816
+ });
1817
+ expect(fs.existsSync(guardianTokenPath(configDir, "desk-box"))).toBe(true);
1818
+ const entry = (
1819
+ readLockfileFromDisk().assistants as Array<Record<string, unknown>>
1820
+ )[0]!;
1821
+ expect(entry).toMatchObject({
1822
+ assistantId: "desk-box",
1823
+ cloud: "paired",
1824
+ paired: true,
1825
+ runtimeUrl: "https://gw.example.com",
1826
+ });
1827
+ });
1828
+
1829
+ test("a nameless re-import adopts the entry the previous release keyed", () => {
1830
+ // The previous release keyed a nameless bundle import on the bundle's own
1831
+ // device id. Re-importing that bundle updates that entry rather than
1832
+ // registering a second one under the gateway-derived id.
1833
+ const bundle = encodeBundle({
1834
+ gatewayUrl: "https://gw.example.com",
1835
+ token: "tok-1",
1836
+ deviceId: "dev-legacy",
1837
+ });
1838
+ writeLockfile({
1839
+ assistants: [
1840
+ {
1841
+ assistantId: "paired-dev-legacy",
1842
+ cloud: "paired",
1843
+ paired: true,
1844
+ runtimeUrl: "https://gw.example.com",
1845
+ },
1846
+ ],
1847
+ });
1848
+
1849
+ const result = connectImport([lockfilePath], configDir, { bundle });
1850
+
1851
+ expect(result).toMatchObject({
1852
+ ok: true,
1853
+ assistantId: "paired-dev-legacy",
1854
+ updated: true,
1855
+ });
1856
+ const entries = readLockfileFromDisk().assistants as Array<
1857
+ Record<string, unknown>
1858
+ >;
1859
+ expect(entries).toHaveLength(1);
1860
+ expect(
1861
+ fs.existsSync(guardianTokenPath(configDir, "paired-dev-legacy")),
1862
+ ).toBe(true);
1863
+ });
1864
+
1865
+ test("a legacy id naming another gateway is not adopted", () => {
1866
+ // The device id is untrusted bundle input, so an entry that does not name
1867
+ // the same gateway is left alone rather than repointed at another host.
1868
+ writeLockfile({
1869
+ assistants: [
1870
+ {
1871
+ assistantId: "paired-dev-legacy",
1872
+ cloud: "paired",
1873
+ paired: true,
1874
+ runtimeUrl: "https://other.example.com",
1875
+ },
1876
+ ],
1877
+ });
1878
+
1879
+ const result = connectImport([lockfilePath], configDir, {
1880
+ bundle: encodeBundle({
1881
+ gatewayUrl: "https://gw.example.com",
1882
+ token: "tok-1",
1883
+ deviceId: "dev-legacy",
1884
+ }),
1885
+ });
1886
+
1887
+ expect(result).toMatchObject({ ok: true, updated: false });
1888
+ const entries = readLockfileFromDisk().assistants as Array<
1889
+ Record<string, unknown>
1890
+ >;
1891
+ expect(entries).toHaveLength(2);
1892
+ expect(
1893
+ (entries.find((e) => e.assistantId === "paired-dev-legacy") ?? {})
1894
+ .runtimeUrl,
1895
+ ).toBe("https://other.example.com");
517
1896
  });
518
1897
 
519
1898
  test("a missing, empty, or non-string bundle is a 400", () => {
@@ -532,7 +1911,7 @@ describe("connectImport", () => {
532
1911
  test("an oversized bundle is refused before decoding", () => {
533
1912
  expect(
534
1913
  connectImport([lockfilePath], configDir, {
535
- bundle: "a".repeat(MAX_PAIR_BUNDLE_LENGTH + 1),
1914
+ bundle: "a".repeat(64 * 1024 + 1),
536
1915
  }),
537
1916
  ).toEqual({
538
1917
  ok: false,
@@ -614,7 +1993,8 @@ describe("connectImport", () => {
614
1993
 
615
1994
  expect(result).toEqual({
616
1995
  ok: true,
617
- assistantId: "paired-dev-nn",
1996
+ assistantId: "paired-gw-example-com",
1997
+ updated: false,
618
1998
  accessOnly: true,
619
1999
  });
620
2000
  });