@socketsecurity/lib 6.0.7 → 6.0.9

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 (323) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +1 -1
  3. package/dist/ai/agent-context.d.mts +103 -0
  4. package/dist/ai/agent-context.js +157 -0
  5. package/dist/ai/backends.d.mts +83 -0
  6. package/dist/ai/backends.js +173 -0
  7. package/dist/ai/credentials.d.mts +49 -0
  8. package/dist/ai/credentials.js +82 -0
  9. package/dist/ai/discover.d.mts +4 -0
  10. package/dist/ai/discover.js +1 -1
  11. package/dist/ai/exec.d.mts +52 -0
  12. package/dist/ai/exec.js +92 -0
  13. package/dist/ai/http.d.mts +132 -0
  14. package/dist/ai/http.js +130 -0
  15. package/dist/ai/profiles.d.mts +41 -6
  16. package/dist/ai/profiles.js +52 -10
  17. package/dist/ai/route.d.mts +82 -0
  18. package/dist/ai/route.js +171 -0
  19. package/dist/ai/spawn.d.mts +42 -2
  20. package/dist/ai/spawn.js +146 -33
  21. package/dist/ai/subagent-status.d.mts +48 -0
  22. package/dist/ai/subagent-status.js +57 -0
  23. package/dist/ai/tier.d.mts +60 -0
  24. package/dist/ai/tier.js +53 -0
  25. package/dist/ai/types.d.mts +25 -2
  26. package/dist/ai/worktree.js +4 -0
  27. package/dist/archives/tar.js +1 -1
  28. package/dist/archives/zip.js +2 -2
  29. package/dist/argv/parse.d.ts +19 -2
  30. package/dist/argv/parse.js +1 -1
  31. package/dist/arrays/join.js +4 -0
  32. package/dist/bin/find.js +4 -4
  33. package/dist/bin/prim.cjs +23322 -18018
  34. package/dist/bin/resolve.js +2 -2
  35. package/dist/cache/ttl/store.js +2 -2
  36. package/dist/cli/check-primordials.d.ts +8 -3
  37. package/dist/cli/check-primordials.js +4 -4
  38. package/dist/compression/_internal.js +1 -1
  39. package/dist/compression/brotli.d.ts +1 -2
  40. package/dist/compression/brotli.js +5 -1
  41. package/dist/compression/gzip.js +5 -1
  42. package/dist/config/layers.d.ts +53 -0
  43. package/dist/config/layers.js +83 -0
  44. package/dist/constants/packages.d.ts +3 -0
  45. package/dist/constants/packages.js +2 -1
  46. package/dist/constants/socket.d.ts +2 -6
  47. package/dist/constants/socket.js +12 -14
  48. package/dist/cover/code.js +2 -2
  49. package/dist/crypto/hash.d.ts +4 -1
  50. package/dist/crypto/hash.js +4 -1
  51. package/dist/dlx/arborist.js +13 -3
  52. package/dist/dlx/binary-cache.js +1 -1
  53. package/dist/dlx/binary-resolution.js +1 -1
  54. package/dist/dlx/detect.d.ts +8 -0
  55. package/dist/dlx/firewall.d.ts +8 -0
  56. package/dist/dlx/lockfile.js +5 -2
  57. package/dist/dlx/manifest.js +1 -1
  58. package/dist/dlx/package.js +4 -0
  59. package/dist/eco/cargo/parse-lockfile.d.ts +1 -2
  60. package/dist/eco/cargo/parse-lockfile.js +3 -3
  61. package/dist/eco/manifest/detect-format.js +1 -1
  62. package/dist/eco/npm/npm/parse-lockfile.d.ts +3 -4
  63. package/dist/eco/npm/npm/parse-lockfile.js +2 -2
  64. package/dist/eco/npm/parse-package-json.d.ts +11 -0
  65. package/dist/eco/npm/parse-package-json.js +1 -1
  66. package/dist/eco/npm/pnpm/parse-lockfile.d.ts +5 -3
  67. package/dist/eco/npm/pnpm/parse-lockfile.js +3 -3
  68. package/dist/eco/npm/yarnpkg/yarn/exec.js +1 -1
  69. package/dist/eco/npm/yarnpkg/yarn/parse-lockfile.d.ts +1 -2
  70. package/dist/eco/npm/yarnpkg/yarn/parse-lockfile.js +2 -2
  71. package/dist/env/proxy.js +1 -1
  72. package/dist/env/rewire.d.ts +6 -3
  73. package/dist/env/rewire.js +7 -8
  74. package/dist/env/socket.d.ts +7 -0
  75. package/dist/env/socket.js +10 -0
  76. package/dist/env/xdg.d.ts +17 -0
  77. package/dist/env/xdg.js +21 -1
  78. package/dist/errors/predicates.js +1 -1
  79. package/dist/errors/stack.js +1 -2
  80. package/dist/external/@npmcli/package-json.js +3455 -4881
  81. package/dist/external/@npmcli/promise-spawn.js +3 -1
  82. package/dist/external/@yarnpkg/extensions.js +1 -1
  83. package/dist/external/external-pack.js +1 -1
  84. package/dist/external/npm-pack.js +29164 -31799
  85. package/dist/external/pico-pack.js +4 -2
  86. package/dist/external/tar-fs.js +63 -16
  87. package/dist/external/which.js +3 -1
  88. package/dist/external-tools/bazel/asset-names.d.ts +1 -1
  89. package/dist/external-tools/bazel/asset-names.js +5 -2
  90. package/dist/external-tools/bazel/from-download.d.ts +1 -1
  91. package/dist/external-tools/bazel/from-download.js +5 -2
  92. package/dist/external-tools/bazel/resolve-bazel-version.js +4 -0
  93. package/dist/external-tools/bazel/resolve.d.ts +3 -3
  94. package/dist/external-tools/bazel/resolve.js +16 -8
  95. package/dist/external-tools/cdxgen/asset-names.d.ts +1 -1
  96. package/dist/external-tools/cdxgen/asset-names.js +5 -2
  97. package/dist/external-tools/cdxgen/from-download.d.ts +1 -1
  98. package/dist/external-tools/cdxgen/from-download.js +7 -4
  99. package/dist/external-tools/cdxgen/resolve.d.ts +3 -3
  100. package/dist/external-tools/cdxgen/resolve.js +16 -8
  101. package/dist/external-tools/from-download.d.ts +2 -2
  102. package/dist/external-tools/from-download.js +11 -5
  103. package/dist/external-tools/from-pip-venv.d.ts +1 -1
  104. package/dist/external-tools/from-pip-venv.js +12 -5
  105. package/dist/external-tools/janus/asset-names.d.ts +1 -1
  106. package/dist/external-tools/janus/asset-names.js +5 -2
  107. package/dist/external-tools/janus/from-download.d.ts +1 -1
  108. package/dist/external-tools/janus/from-download.js +5 -2
  109. package/dist/external-tools/janus/resolve.d.ts +3 -3
  110. package/dist/external-tools/janus/resolve.js +16 -8
  111. package/dist/external-tools/jre/asset-names.d.ts +1 -1
  112. package/dist/external-tools/jre/asset-names.js +5 -2
  113. package/dist/external-tools/jre/detect-platform-arch.js +1 -1
  114. package/dist/external-tools/jre/from-download.d.ts +1 -1
  115. package/dist/external-tools/jre/from-download.js +7 -4
  116. package/dist/external-tools/jre/from-java-home.js +2 -2
  117. package/dist/external-tools/jre/from-vfs.js +2 -2
  118. package/dist/external-tools/jre/resolve.d.ts +3 -3
  119. package/dist/external-tools/jre/resolve.js +16 -8
  120. package/dist/external-tools/manifest.d.ts +18 -0
  121. package/dist/external-tools/manifest.js +2 -2
  122. package/dist/external-tools/opengrep/asset-names.d.ts +1 -1
  123. package/dist/external-tools/opengrep/asset-names.js +5 -2
  124. package/dist/external-tools/opengrep/from-download.d.ts +1 -1
  125. package/dist/external-tools/opengrep/from-download.js +5 -2
  126. package/dist/external-tools/opengrep/resolve.d.ts +3 -3
  127. package/dist/external-tools/opengrep/resolve.js +16 -8
  128. package/dist/external-tools/python/asset-names.d.ts +1 -1
  129. package/dist/external-tools/python/asset-names.js +11 -4
  130. package/dist/external-tools/python/dlx.d.ts +3 -3
  131. package/dist/external-tools/python/dlx.js +20 -9
  132. package/dist/external-tools/python/from-download.d.ts +1 -1
  133. package/dist/external-tools/python/from-download.js +12 -5
  134. package/dist/external-tools/python/pin.js +6 -3
  135. package/dist/external-tools/python/pip-install.js +6 -3
  136. package/dist/external-tools/python/resolve.d.ts +3 -3
  137. package/dist/external-tools/python/resolve.js +19 -11
  138. package/dist/external-tools/python/uv-install.d.ts +89 -0
  139. package/dist/external-tools/python/uv-install.js +165 -0
  140. package/dist/external-tools/sbt/asset-names.d.ts +1 -1
  141. package/dist/external-tools/sbt/asset-names.js +5 -2
  142. package/dist/external-tools/sbt/from-download.d.ts +1 -1
  143. package/dist/external-tools/sbt/from-download.js +5 -2
  144. package/dist/external-tools/sbt/resolve.d.ts +3 -3
  145. package/dist/external-tools/sbt/resolve.js +16 -8
  146. package/dist/external-tools/skillspector/from-dlx.d.ts +1 -1
  147. package/dist/external-tools/skillspector/from-dlx.js +10 -3
  148. package/dist/external-tools/skillspector/from-uv.d.ts +30 -0
  149. package/dist/external-tools/skillspector/from-uv.js +56 -0
  150. package/dist/external-tools/skillspector/resolve.d.ts +20 -5
  151. package/dist/external-tools/skillspector/resolve.js +27 -6
  152. package/dist/external-tools/skillspector/types.d.ts +3 -1
  153. package/dist/external-tools/synp/asset-names.d.ts +1 -1
  154. package/dist/external-tools/synp/asset-names.js +6 -2
  155. package/dist/external-tools/synp/from-download.d.ts +1 -1
  156. package/dist/external-tools/synp/from-download.js +5 -2
  157. package/dist/external-tools/synp/resolve.d.ts +3 -3
  158. package/dist/external-tools/synp/resolve.js +16 -8
  159. package/dist/external-tools/trivy/asset-names.d.ts +1 -1
  160. package/dist/external-tools/trivy/asset-names.js +5 -2
  161. package/dist/external-tools/trivy/from-download.d.ts +1 -1
  162. package/dist/external-tools/trivy/from-download.js +7 -4
  163. package/dist/external-tools/trivy/resolve.d.ts +3 -3
  164. package/dist/external-tools/trivy/resolve.js +16 -8
  165. package/dist/external-tools/trufflehog/asset-names.d.ts +1 -1
  166. package/dist/external-tools/trufflehog/asset-names.js +5 -2
  167. package/dist/external-tools/trufflehog/from-download.d.ts +1 -1
  168. package/dist/external-tools/trufflehog/from-download.js +7 -4
  169. package/dist/external-tools/trufflehog/resolve.d.ts +3 -3
  170. package/dist/external-tools/trufflehog/resolve.js +16 -8
  171. package/dist/external-tools/uv/asset-names.d.ts +36 -0
  172. package/dist/external-tools/uv/asset-names.js +73 -0
  173. package/dist/external-tools/uv/from-download.d.ts +17 -0
  174. package/dist/external-tools/uv/from-download.js +50 -0
  175. package/dist/external-tools/uv/from-path.d.ts +5 -0
  176. package/dist/external-tools/uv/from-path.js +22 -0
  177. package/dist/external-tools/uv/from-vfs.d.ts +7 -0
  178. package/dist/external-tools/uv/from-vfs.js +26 -0
  179. package/dist/external-tools/uv/resolve.d.ts +25 -0
  180. package/dist/external-tools/uv/resolve.js +61 -0
  181. package/dist/external-tools/uv/types.d.ts +24 -0
  182. package/dist/external-tools/uv/types.js +2 -0
  183. package/dist/fleet/repo-config.d.ts +53 -0
  184. package/dist/fleet/repo-config.js +79 -0
  185. package/dist/fs/allowed-dirs-cache.d.ts +27 -1
  186. package/dist/fs/allowed-dirs-cache.js +39 -6
  187. package/dist/fs/copy.d.ts +88 -0
  188. package/dist/fs/copy.js +89 -0
  189. package/dist/fs/find.js +1 -1
  190. package/dist/fs/read-json-cache.d.ts +7 -0
  191. package/dist/fs/resolve-module.js +6 -2
  192. package/dist/fs/safe.js +1 -1
  193. package/dist/git/_internal.js +3 -3
  194. package/dist/git/repo.js +2 -4
  195. package/dist/git/staged.js +8 -0
  196. package/dist/git/tracked.d.ts +84 -0
  197. package/dist/git/tracked.js +163 -0
  198. package/dist/git/unstaged.js +8 -0
  199. package/dist/github/refs-graphql.js +4 -0
  200. package/dist/github/refs-rest.js +4 -0
  201. package/dist/globs/_internal.js +1 -1
  202. package/dist/globs/match.js +9 -1
  203. package/dist/globs/matcher.js +5 -1
  204. package/dist/http-request/browser.js +7 -3
  205. package/dist/http-request/checksum-file.js +1 -1
  206. package/dist/http-request/{browser-fetch.d.ts → fetch/browser.d.ts} +2 -2
  207. package/dist/http-request/{browser-fetch.js → fetch/browser.js} +4 -4
  208. package/dist/http-request/headers.js +1 -1
  209. package/dist/http-request/request-attempt.js +2 -2
  210. package/dist/http-request/user-agent.js +1 -1
  211. package/dist/integrity.d.ts +124 -71
  212. package/dist/integrity.js +168 -78
  213. package/dist/json/edit.js +38 -30
  214. package/dist/json/format.js +1 -1
  215. package/dist/logger/colors.d.ts +1 -2
  216. package/dist/logger/colors.js +2 -2
  217. package/dist/logger/symbols-builder.js +7 -8
  218. package/dist/logger/symbols.js +8 -8
  219. package/dist/native-messaging/install.d.ts +1 -1
  220. package/dist/native-messaging/install.js +7 -4
  221. package/dist/native-messaging/rate-limit.d.ts +7 -0
  222. package/dist/native-messaging/rate-limit.js +4 -0
  223. package/dist/node/async-hooks.js +1 -1
  224. package/dist/node/child-process.js +1 -1
  225. package/dist/node/crypto.js +1 -1
  226. package/dist/node/events.js +1 -1
  227. package/dist/node/fs-promises.js +1 -1
  228. package/dist/node/fs.d.ts +22 -6
  229. package/dist/node/fs.js +16 -3
  230. package/dist/node/http.js +1 -1
  231. package/dist/node/https.js +1 -1
  232. package/dist/node/module.d.ts +73 -2
  233. package/dist/node/module.js +97 -12
  234. package/dist/node/os.d.ts +10 -2
  235. package/dist/node/os.js +11 -4
  236. package/dist/node/path.d.ts +11 -2
  237. package/dist/node/path.js +17 -4
  238. package/dist/node/timers-promises.js +1 -1
  239. package/dist/node/url.js +1 -1
  240. package/dist/node/util.js +1 -1
  241. package/dist/objects/getters.js +1 -1
  242. package/dist/objects/mutate.js +2 -2
  243. package/dist/packages/edit-class.d.ts +2 -3
  244. package/dist/packages/edit-class.js +41 -35
  245. package/dist/packages/exports.js +4 -4
  246. package/dist/packages/fetch.js +1 -1
  247. package/dist/packages/isolation.js +2 -2
  248. package/dist/packages/licenses.js +2 -2
  249. package/dist/packages/manifest.d.ts +29 -0
  250. package/dist/packages/manifest.js +40 -4
  251. package/dist/packages/normalize.js +2 -2
  252. package/dist/packages/provenance.js +3 -3
  253. package/dist/packages/specs.js +1 -1
  254. package/dist/packages/tarball.js +4 -2
  255. package/dist/packages/types.d.ts +1 -2
  256. package/dist/paths/_internal.d.ts +1 -7
  257. package/dist/paths/_internal.js +9 -15
  258. package/dist/paths/conversion.js +1 -1
  259. package/dist/paths/dirnames.d.ts +1 -0
  260. package/dist/paths/dirnames.js +2 -0
  261. package/dist/paths/normalize.js +1 -1
  262. package/dist/paths/predicates.js +2 -1
  263. package/dist/paths/resolve.js +14 -19
  264. package/dist/paths/rewire.d.ts +5 -0
  265. package/dist/paths/socket.d.ts +137 -123
  266. package/dist/paths/socket.js +172 -131
  267. package/dist/perf/metrics.js +1 -1
  268. package/dist/perf/report.js +1 -1
  269. package/dist/primordials/process.d.ts +88 -0
  270. package/dist/primordials/process.js +132 -0
  271. package/dist/primordials/uncurry.d.ts +1 -2
  272. package/dist/process/spawn/child.js +8 -2
  273. package/dist/process/spawn/errors.js +1 -1
  274. package/dist/releases/github-archives.js +1 -1
  275. package/dist/releases/github-listing.d.ts +1 -2
  276. package/dist/schema/types.d.ts +3 -4
  277. package/dist/schema/validate.js +1 -1
  278. package/dist/secrets/broker.d.ts +35 -0
  279. package/dist/secrets/broker.js +72 -0
  280. package/dist/secrets/find.d.ts +8 -6
  281. package/dist/secrets/find.js +25 -5
  282. package/dist/secrets/keychain.d.ts +1 -1
  283. package/dist/secrets/linux.js +32 -44
  284. package/dist/secrets/macos.d.ts +1 -2
  285. package/dist/secrets/macos.js +20 -29
  286. package/dist/secrets/rc.d.ts +2 -2
  287. package/dist/secrets/rc.js +21 -13
  288. package/dist/secrets/socket-api-token.js +8 -0
  289. package/dist/secrets/windows.js +27 -33
  290. package/dist/shell/parse.d.ts +32 -0
  291. package/dist/shell/parse.js +60 -0
  292. package/dist/smol/detect.js +1 -1
  293. package/dist/smol/http.js +1 -1
  294. package/dist/smol/https.js +1 -1
  295. package/dist/smol/manifest.js +1 -1
  296. package/dist/smol/path.js +1 -1
  297. package/dist/smol/primordial.js +1 -1
  298. package/dist/smol/purl.js +1 -1
  299. package/dist/smol/versions.js +1 -1
  300. package/dist/smol/vfs.js +1 -1
  301. package/dist/sorts/_internal.d.ts +1 -2
  302. package/dist/sorts/_internal.js +2 -6
  303. package/dist/sorts/semver.js +2 -2
  304. package/dist/spinner/create-spinner-class.js +2 -2
  305. package/dist/spinner/spinner-internals.d.ts +1 -1
  306. package/dist/spinner/spinner-internals.js +9 -5
  307. package/dist/spinner/spinner.d.ts +4 -0
  308. package/dist/spinner/spinner.js +1 -1
  309. package/dist/stdio/progress.js +6 -2
  310. package/dist/stdio/prompts.d.ts +2 -10
  311. package/dist/stdio/prompts.js +11 -24
  312. package/dist/strings/format.js +1 -1
  313. package/dist/strings/search.js +1 -1
  314. package/dist/temporal/instant.js +2 -2
  315. package/dist/themes/context.d.ts +5 -3
  316. package/dist/themes/context.js +5 -6
  317. package/dist/url/assert-safe.d.ts +29 -0
  318. package/dist/url/assert-safe.js +54 -0
  319. package/dist/url/predicates.d.ts +31 -1
  320. package/dist/url/predicates.js +42 -1
  321. package/dist/url/types.d.ts +4 -0
  322. package/llms.txt +750 -0
  323. package/package.json +253 -125
package/CHANGELOG.md CHANGED
@@ -5,6 +5,48 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [6.0.9](https://github.com/SocketDev/socket-lib/releases/tag/v6.0.9) - 2026-06-18
9
+
10
+ ### Added
11
+
12
+ - **`external-tools/uv` — uv resolver.** Resolves Astral's `uv` Python package manager across three tiers (embedded VFS, then system PATH, then a downloaded GitHub release), matching the shape of the other `external-tools/*` tool resolvers. Exports `resolveUv`, `ResolvedUv`, `UvSource`, plus the per-platform asset map and download helpers.
13
+ - **`external-tools/python/uv-install` — reproducible uv-project install helpers.** `uvSyncProject` installs a uv project at its exact pinned versions and refuses to proceed when those have drifted, so every machine gets the same result. `uvExportMaterialize` installs the same pinned versions into a content-addressed directory with no virtualenv, so the result is relocatable and embeddable in a single-file build. Concurrent callers serialize so two installs can't collide. The Python analog of the npm dlx install model.
14
+ - **`external-tools/skillspector` — pinned-project resolution tier.** `skillspectorFromUv` installs SkillSpector from a fully pinned uv project (every version fixed) and returns its entry point, resolved ahead of the existing git-SHA fallback when a project directory and `uv` binary are supplied. Adds a `'uv'` source to the resolution result.
15
+ - **`config/layers` — generic layered-config reader.** `readConfigLayers(name, { dirs })` reads a named config file from an ordered list of layer directories (lowest precedence first) and returns the layers that exist; absent or unparseable layers are skipped. `mergeConfigArray` concatenates one array-valued key across all layers, for lists that higher layers extend rather than replace. It carries no project-convention knowledge: the caller supplies the directories and the merge policy.
16
+ - **`paths` — `_wheelhouse` tool-layout dirs and the agent-clone dir.** `getSocketRackDir` and `getSocketRackToolDir` locate the racked tool store, `getSocketWheelhouseBinDir` the PATH-handle directory that points into it, and `getSocketRepoClonesDir` the directory where agents clone external repos for reference (kept out of the projects tree so sibling-walking tooling never treats a clone as a fleet member).
17
+ - **`ai` — offline/gated-model detection and fall-over.** `spawnTierWithFallback` walks a tier's cross-engine equivalence chain and runs the first engine that is both installed and authenticated, so a request still completes when the preferred model is down, gated, or unkeyed. `isModelUnavailable` recognizes a down-or-gated model from the engine's actual output rather than a brittle literal-string match. The `ai/route` resolver and `ai/subagent-status` reader are exposed as their own entry points.
18
+ - **`fs/copy` — recursive copy with three destination modes.** `copy(from, to, { mode })` copies a file or directory tree. `CopyMode` chooses how an existing destination is treated: `'overlay'` (the default: overwrite collisions, keep destination-only files), `'pave'` (the destination becomes an exact mirror of the source via an atomic sibling-temp-then-rename swap, so no stale files survive and a partial tree is never observed), or `'fill'` (no-clobber: add only what is missing, never overwrite). Also accepts `filter`, `dereference`, and an abort `signal`.
19
+ - **`node/requireFrom` — relative `require` bound to the caller's directory.** `requireFrom(fromUrl, specifier)` resolves a relative specifier (`./x`, `../y`) from the caller's own directory, passed as `import.meta.url`, so relative loads bind to the right base when modules run unbundled (for example AOT-compiled from source, where each module sits at its own nested path). Builtins and bare packages resolve as before.
20
+ - **`secrets` — proteus broker credential tier.** The credential resolver gains a broker layer in the documented order (explicit, then env, then broker, then keychain): it connects to the broker's runtime socket, requests the value, and self-gates. No socket means no broker, so it returns `undefined` and falls through to the keychain. Async only (absent from `resolveSync`) and skipped under `allowEnvOnly`. Existing `resolveProviderCredential` call sites are unchanged.
21
+ - **`paths` — `getRuntimeSocketPath` and `getXdgRuntimeDir` for daemon sockets.** One resolver a daemon and its clients both call to locate a runtime socket: the XDG runtime dir when present, a `$TMPDIR/<name>-<uid>` fallback, or a named pipe on Windows.
22
+ - **`llms.txt` — discovery index for AI agents.** `pnpm run docs` now also emits a publish-safe `llms.txt` at the package root that links each export subpath to its shipped `.d.mts` declaration, giving an agent one file to read after installing. It ships in the published tarball.
23
+ - **`packages/manifest` — `trimPublishManifest`.** Returns a shallow copy of a `package.json` that omits dev/build-only top-level fields (`devDependencies`, `scripts` by default) so a published tarball and its npm metadata stay lean. `drop` overrides the field set; `keep` retains a field even when dropped (e.g. a runtime `postinstall`). The original object is left intact.
24
+
25
+ ### Changed
26
+
27
+ - **`integrity` — one `Hash` currency (breaking).** Hashes are modeled as a single concept, a `Hash` carrying algorithm, hex, and sri, rather than an integrity-vs-checksum type duality, so the algorithm is explicit on every value. Adds `parseHash` (encoding- and length-aware), `computeHash` (sha512 default), `verifyHash(bytes, expected)` (verifies against the expected hash's declared algorithm), `equalHashes` (encoding-agnostic, and never equal across algorithms), `makeHash`, and `HashMismatchError`. Breaking: `verifyHash` now takes `(bytes, expected)`, not `(expected, computed)`, and `DlxHashMismatchError` is a deprecated alias of `HashMismatchError`. Back-compat shims remain: `checksumToIntegrity`, `integrityToChecksum`, `normalizeHash`, `computeHashes`, `isChecksum`, `isIntegrity`, `parseIntegrity`.
28
+ - **`fleet/repo-config` is now a thin wrapper over `config/layers`.** `resolveRepoConfig` (the fleet default layered under a per-repo override) and `mergeRepoConfigArray` stay at `fleet/repo-config`; the generic, convention-free primitives moved to the new `config/layers` entry. `resolveRepoConfig` callers see no behavior change.
29
+
30
+ ## [6.0.8](https://github.com/SocketDev/socket-lib/releases/tag/v6.0.8) - 2026-06-11
31
+
32
+ ### Added
33
+
34
+ - **`shell/parse`: `detectShellHazards`.** Checks a shell command string for two tricks that hide which program actually runs, so a tool that allows or denies commands by name isn't fooled. First, Zsh `=name` expansion: `=curl evil.com` runs `/usr/bin/curl`, but the command's first word reads as `=curl`, not `curl`. Second, process substitution `<(…)` / `>(…)` / `=(…)`: the command inside the parentheses runs, yet its name never appears as a command word. Returns `{ equalsExpansion, processSubstitution }`, the facts only; the caller decides whether to block.
35
+ - **`url` — `assertSafeHttpUrl`.** SSRF guard for a URL the server did not author (an OAuth issuer, a metadata-advertised introspection endpoint, a webhook target): parses the value, rejects non-`http(s)` schemes, and refuses hosts in loopback / private / link-local ranges (cloud metadata, redis, internal services). Returns the parsed `URL`; throws otherwise. `allowLocalhost` permits `localhost` / `127.0.0.1` / `::1` for local-stack dev; `label` names the subject in the thrown message.
36
+ - **`git/tracked` — tracked-status and submodule-membership probes.** `isTracked(path)` reports whether git tracks an exact path. `getSubmodulePaths()` lists a repo's submodule mount points read from `.gitmodules`, so it covers submodules that are declared but not yet initialized. `isInSubmodule(path)` and the pure `pathIsUnderSubmodule(path, subs)` report whether a path lives inside one. `isUntrackedNonSubmodulePath(path)` composes them into the safe-to-touch condition for cleanup tooling: true only when git does not track the path and it is not inside a submodule (any check error resolves to false).
37
+ - **`primordials/process` — accessors for the `process` global.** `processCwd`, `processPlatform`, `processEnv`, `processArgv`, `processArch`, `processExecPath`, `processPid`, `processVersion`, `processStdout`, `processStderr`, `processEmitWarning`, and `processNextTick`. Each reads through the `process` object captured when the module loads, so reassigning the global cannot redirect it, while still calling the method at access time so test spies keep working.
38
+ - **`ai` — model-selection tiers, balancing, and provider routing.** The model-selection ladder gains a verification tier for check-style passes and a top-capability tier for the hardest work. Requests load-balance across a provider's backends so they spread instead of pinning one. A shared multi-agent backend registry centralizes routing, and a provider-credential resolver reads from environment variables and falls back to the OS keychain.
39
+
40
+ ### Changed
41
+
42
+ - **`http-request` browser entry — `fetch/browser`.** The browser build of `httpJson` / `httpText` now resolves through `http-request/fetch/browser` (was `http-request/browser-fetch`), and the package's `browser` field maps Node-only builtins to their browser stubs. Bundlers targeting the browser pick the right entry automatically.
43
+ - **Node-builtin accessors are browser-bundler friendly.** The internal Node-builtin accessor layer requires builtins by their bare name so a browser bundler's builtin replacement (the package `browser` field, a consumer's bundler fallback config) resolves them; the `node:`-prefixed form bypasses that replacement in some bundlers. No public API change.
44
+ - **Caller-supplied `options` are prototype-pollution hardened.** Functions that take an `options` argument normalize it before reading, so an object with a tampered prototype cannot leak inherited properties into the library's behavior.
45
+
46
+ ### Fixed
47
+
48
+ - **`ai` — codex reasoning effort.** Setting `effort` on a `spawnAiAgent` call now reaches the codex backend (emitted as codex's reasoning-effort config), where it was previously accepted but silently ignored for every agent except claude. The claude-only `max` level maps to codex's `xhigh` ceiling.
49
+
8
50
  ## [6.0.7](https://github.com/SocketDev/socket-lib/releases/tag/v6.0.7) - 2026-06-03
9
51
 
10
52
  ### Added
package/README.md CHANGED
@@ -39,7 +39,7 @@ import { httpJson } from '@socketsecurity/lib/http-request'
39
39
  import { safeDelete } from '@socketsecurity/lib/fs'
40
40
  ```
41
41
 
42
- Start with the [API Index](./docs/api-index.md) — every subpath export with a one-line description.
42
+ Start with the [API reference](./docs/api.md) — every subpath export with a one-line description.
43
43
 
44
44
  ## Development
45
45
 
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @file Agent-awareness for hooks + scripts: WHICH agent is running right now,
3
+ * and WHERE that agent keeps its config / memory on the current platform. Two
4
+ * distinct questions, two functions:
5
+ *
6
+ * - `detectAgent()` — who is invoking this process? Read from the environment
7
+ * the running agent injects, NOT from any stdin payload (a Claude Code hook
8
+ * gets `{tool_name,…}` on stdin but no agent id; Codex/OpenCode use
9
+ * entirely different invocation contracts). The cross-agent signal is
10
+ * `AI_AGENT` (Claude Code sets `AI_AGENT=claude-code_<ver>_agent`);
11
+ * tool-specific flags (`CLAUDECODE`, `CODEX_*`, `OPENCODE`) are the
12
+ * fallback. Returns `undefined` when nothing identifies an agent (a plain
13
+ * shell / CI).
14
+ * - `agentPaths()` — given an agent, the config + memory directories it uses on
15
+ * THIS OS. Built on the cross-platform `getHome()` (HOME → USERPROFILE) and
16
+ * XDG helpers so a Windows path differs from mac/linux correctly. This is
17
+ * the runtime complement to `discoverAiAgents()` (which agents are
18
+ * INSTALLED on PATH); this answers which one is DRIVING + where it lives.
19
+ * Memory caveat baked into the data: only Claude Code maintains an
20
+ * agent-written memory store (`~/.claude/projects/<slug>/memory/`). Codex +
21
+ * OpenCode have NO self-written memory — their only persistent context is
22
+ * the human-authored AGENTS.md. So `agentPaths(...).memoryDir` is defined
23
+ * only for `claude`; for the others it is `undefined` (there is no memory
24
+ * dir to point at), and the shared cross-tool memory surface is the
25
+ * committed AGENTS.md (which the fleet symlinks to CLAUDE.md).
26
+ */
27
+ import type { AiAgentName } from './types.mts';
28
+ /**
29
+ * The detected running agent + the raw version token from `AI_AGENT`, when
30
+ * present. `agent` is the normalized `AiAgentName`; `raw` is the full env value
31
+ * (e.g. `claude-code_2-1-169_agent`) for callers that want the version.
32
+ */
33
+ export interface DetectedAgent {
34
+ readonly agent: AiAgentName;
35
+ readonly raw: string | undefined;
36
+ }
37
+ export declare function agentFromAiAgentEnv(value: string): AiAgentName | undefined;
38
+ /**
39
+ * The config + memory directories an agent uses on the current platform.
40
+ *
41
+ * `configDir` is where the agent keeps global config / instructions.
42
+ * `memoryDir` is the agent-written persistent-memory store — defined ONLY for
43
+ * `claude` (Codex/OpenCode have no self-written memory; their durable context
44
+ * is the human-authored AGENTS.md). For non-claude agents `memoryDir` is
45
+ * `undefined`.
46
+ *
47
+ * All paths derive from `getHome()` (HOME → USERPROFILE, cross-platform) so the
48
+ * Windows location differs from mac/linux correctly. Returns `undefined` when
49
+ * the home dir can't be resolved.
50
+ */
51
+ export interface AgentPaths {
52
+ readonly agent: AiAgentName;
53
+ readonly configDir: string;
54
+ readonly memoryDir: string | undefined;
55
+ }
56
+ /**
57
+ * Resolve an agent's config (and, for Claude, memory) directories on this OS.
58
+ *
59
+ * Per-agent / per-platform (verified against each tool's docs; flagged where a
60
+ * platform path is best-effort):
61
+ *
62
+ * - **claude**: `~/.claude` on every OS. Memory:
63
+ * `~/.claude/projects/<cwd-slug>/memory/` (slug = cwd with `/`→`-`). Pass
64
+ * `options.cwd` to compute the memory dir for a specific project.
65
+ * - **codex**: `$CODEX_HOME` if set, else `~/.codex` (all OSes, incl. Windows
66
+ * `%USERPROFILE%\.codex` — Codex uses a dotdir, not %APPDATA%). No memory.
67
+ * - **opencode**: XDG — `$XDG_CONFIG_HOME/opencode` else `~/.config/opencode` on
68
+ * mac/linux; on Windows `%APPDATA%\opencode` (best-effort: OpenCode's docs
69
+ * don't pin the Windows user-config path; APPDATA is the conventional
70
+ * fallback and is overridable via `$XDG_CONFIG_HOME`). No memory.
71
+ * - **gemini**: `~/.gemini` (all OSes). No memory.
72
+ *
73
+ * @returns The resolved paths, or `undefined` if the home dir is unresolvable.
74
+ */
75
+ export declare function agentPaths(agent: AiAgentName, options?: {
76
+ cwd?: string | undefined;
77
+ } | undefined): AgentPaths | undefined;
78
+ /**
79
+ * Detect which AI agent is invoking the current process, from the environment.
80
+ *
81
+ * Resolution order:
82
+ *
83
+ * 1. `AI_AGENT` — the cross-agent signal (Claude Code sets it). Its leading token
84
+ * names the family.
85
+ * 2. Tool-specific flags as a fallback: `CLAUDECODE=1` / `CLAUDE_CODE_*` → claude;
86
+ * `CODEX_*` → codex; `OPENCODE` → opencode.
87
+ *
88
+ * Returns `undefined` when no agent signal is present (a plain shell, CI, a
89
+ * non-agent subprocess) — callers should treat that as "agent-agnostic", not an
90
+ * error.
91
+ *
92
+ * Note: a hook receives NO agent id in its stdin payload; this env read is the
93
+ * only reliable signal. Different agents also invoke hooks differently (Claude:
94
+ * stdin JSON; Codex: its own hooks; OpenCode: plugin callbacks), so a
95
+ * `.claude/hooks/` script is fundamentally Claude-invoked — `detectAgent()` is
96
+ * most useful for scripts/skills that want to branch on the active agent, or
97
+ * when an agent delegates to another.
98
+ *
99
+ * @example
100
+ * const detected = detectAgent()
101
+ * if (detected?.agent === 'claude') { ... }
102
+ */
103
+ export declare function detectAgent(): DetectedAgent | undefined;
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with rolldown */
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_runtime = require('../_virtual/_rolldown/runtime.js');
5
+ const require_constants_platform = require('../constants/platform.js');
6
+ const require_env_rewire = require('../env/rewire.js');
7
+ const require_env_home = require('../env/home.js');
8
+ const require_env_xdg = require('../env/xdg.js');
9
+ let node_path = require("node:path");
10
+ node_path = require_runtime.__toESM(node_path, 1);
11
+
12
+ //#region src/ai/agent-context.mts
13
+ /**
14
+ * @file Agent-awareness for hooks + scripts: WHICH agent is running right now,
15
+ * and WHERE that agent keeps its config / memory on the current platform. Two
16
+ * distinct questions, two functions:
17
+ *
18
+ * - `detectAgent()` — who is invoking this process? Read from the environment
19
+ * the running agent injects, NOT from any stdin payload (a Claude Code hook
20
+ * gets `{tool_name,…}` on stdin but no agent id; Codex/OpenCode use
21
+ * entirely different invocation contracts). The cross-agent signal is
22
+ * `AI_AGENT` (Claude Code sets `AI_AGENT=claude-code_<ver>_agent`);
23
+ * tool-specific flags (`CLAUDECODE`, `CODEX_*`, `OPENCODE`) are the
24
+ * fallback. Returns `undefined` when nothing identifies an agent (a plain
25
+ * shell / CI).
26
+ * - `agentPaths()` — given an agent, the config + memory directories it uses on
27
+ * THIS OS. Built on the cross-platform `getHome()` (HOME → USERPROFILE) and
28
+ * XDG helpers so a Windows path differs from mac/linux correctly. This is
29
+ * the runtime complement to `discoverAiAgents()` (which agents are
30
+ * INSTALLED on PATH); this answers which one is DRIVING + where it lives.
31
+ * Memory caveat baked into the data: only Claude Code maintains an
32
+ * agent-written memory store (`~/.claude/projects/<slug>/memory/`). Codex +
33
+ * OpenCode have NO self-written memory — their only persistent context is
34
+ * the human-authored AGENTS.md. So `agentPaths(...).memoryDir` is defined
35
+ * only for `claude`; for the others it is `undefined` (there is no memory
36
+ * dir to point at), and the shared cross-tool memory surface is the
37
+ * committed AGENTS.md (which the fleet symlinks to CLAUDE.md).
38
+ */
39
+ function agentFromAiAgentEnv(value) {
40
+ const lower = value.toLowerCase();
41
+ if (lower.startsWith("claude")) return "claude";
42
+ if (lower.startsWith("codex")) return "codex";
43
+ if (lower.startsWith("opencode")) return "opencode";
44
+ if (lower.startsWith("gemini")) return "gemini";
45
+ }
46
+ /**
47
+ * Resolve an agent's config (and, for Claude, memory) directories on this OS.
48
+ *
49
+ * Per-agent / per-platform (verified against each tool's docs; flagged where a
50
+ * platform path is best-effort):
51
+ *
52
+ * - **claude**: `~/.claude` on every OS. Memory:
53
+ * `~/.claude/projects/<cwd-slug>/memory/` (slug = cwd with `/`→`-`). Pass
54
+ * `options.cwd` to compute the memory dir for a specific project.
55
+ * - **codex**: `$CODEX_HOME` if set, else `~/.codex` (all OSes, incl. Windows
56
+ * `%USERPROFILE%\.codex` — Codex uses a dotdir, not %APPDATA%). No memory.
57
+ * - **opencode**: XDG — `$XDG_CONFIG_HOME/opencode` else `~/.config/opencode` on
58
+ * mac/linux; on Windows `%APPDATA%\opencode` (best-effort: OpenCode's docs
59
+ * don't pin the Windows user-config path; APPDATA is the conventional
60
+ * fallback and is overridable via `$XDG_CONFIG_HOME`). No memory.
61
+ * - **gemini**: `~/.gemini` (all OSes). No memory.
62
+ *
63
+ * @returns The resolved paths, or `undefined` if the home dir is unresolvable.
64
+ */
65
+ function agentPaths(agent, options) {
66
+ const opts = {
67
+ __proto__: null,
68
+ ...options
69
+ };
70
+ const home = require_env_home.getHome();
71
+ if (!home) return;
72
+ switch (agent) {
73
+ case "claude": {
74
+ const configDir = node_path.default.join(home, ".claude");
75
+ const cwd = opts.cwd;
76
+ return {
77
+ agent,
78
+ configDir,
79
+ memoryDir: cwd ? node_path.default.join(configDir, "projects", cwd.replace(/[/\\]/g, "-"), "memory") : void 0
80
+ };
81
+ }
82
+ case "codex": return {
83
+ agent,
84
+ configDir: require_env_rewire.getEnvValue("CODEX_HOME") || node_path.default.join(home, ".codex"),
85
+ memoryDir: void 0
86
+ };
87
+ case "opencode": {
88
+ const xdg = require_env_xdg.getXdgConfigHome();
89
+ let base;
90
+ if (xdg) base = xdg;
91
+ else if (require_constants_platform.WIN32) base = require_env_rewire.getEnvValue("APPDATA") || node_path.default.join(home, ".config");
92
+ else base = node_path.default.join(home, ".config");
93
+ return {
94
+ agent,
95
+ configDir: node_path.default.join(base, "opencode"),
96
+ memoryDir: void 0
97
+ };
98
+ }
99
+ case "gemini": return {
100
+ agent,
101
+ configDir: node_path.default.join(home, ".gemini"),
102
+ memoryDir: void 0
103
+ };
104
+ }
105
+ }
106
+ /**
107
+ * Detect which AI agent is invoking the current process, from the environment.
108
+ *
109
+ * Resolution order:
110
+ *
111
+ * 1. `AI_AGENT` — the cross-agent signal (Claude Code sets it). Its leading token
112
+ * names the family.
113
+ * 2. Tool-specific flags as a fallback: `CLAUDECODE=1` / `CLAUDE_CODE_*` → claude;
114
+ * `CODEX_*` → codex; `OPENCODE` → opencode.
115
+ *
116
+ * Returns `undefined` when no agent signal is present (a plain shell, CI, a
117
+ * non-agent subprocess) — callers should treat that as "agent-agnostic", not an
118
+ * error.
119
+ *
120
+ * Note: a hook receives NO agent id in its stdin payload; this env read is the
121
+ * only reliable signal. Different agents also invoke hooks differently (Claude:
122
+ * stdin JSON; Codex: its own hooks; OpenCode: plugin callbacks), so a
123
+ * `.claude/hooks/` script is fundamentally Claude-invoked — `detectAgent()` is
124
+ * most useful for scripts/skills that want to branch on the active agent, or
125
+ * when an agent delegates to another.
126
+ *
127
+ * @example
128
+ * const detected = detectAgent()
129
+ * if (detected?.agent === 'claude') { ... }
130
+ */
131
+ function detectAgent() {
132
+ const aiAgent = require_env_rewire.getEnvValue("AI_AGENT");
133
+ if (aiAgent) {
134
+ const agent = agentFromAiAgentEnv(aiAgent);
135
+ if (agent) return {
136
+ agent,
137
+ raw: aiAgent
138
+ };
139
+ }
140
+ if (require_env_rewire.getEnvValue("CLAUDECODE") || require_env_rewire.getEnvValue("CLAUDE_CODE_ENTRYPOINT")) return {
141
+ agent: "claude",
142
+ raw: aiAgent
143
+ };
144
+ if (require_env_rewire.getEnvValue("OPENCODE")) return {
145
+ agent: "opencode",
146
+ raw: aiAgent
147
+ };
148
+ if (require_env_rewire.getEnvValue("CODEX_SANDBOX") || require_env_rewire.getEnvValue("CODEX_HOME")) return {
149
+ agent: "codex",
150
+ raw: aiAgent
151
+ };
152
+ }
153
+
154
+ //#endregion
155
+ exports.agentFromAiAgentEnv = agentFromAiAgentEnv;
156
+ exports.agentPaths = agentPaths;
157
+ exports.detectAgent = detectAgent;
@@ -0,0 +1,83 @@
1
+ /**
2
+ * @file Multi-agent CLI backend registry + role routing. The fleet's review /
3
+ * scan / fix skills delegate work to whichever agent CLIs are installed
4
+ * (`codex`, `claude`, `kimi`, `opencode`), falling back through a per-role
5
+ * preference order and skipping a pass when nothing usable is present. Before
6
+ * this module each skill copied the same registry + detection block inline
7
+ * (the canonical copy lived in
8
+ * `.claude/skills/fleet/reviewing-code/run.mts`); the shared policy doc
9
+ * (`_shared/multi-agent-backends.md`) flagged the extraction. Import
10
+ * `BACKENDS` / `detectAvailableBackends` / `resolveBackendForRole` here
11
+ * instead so a new backend or a routing-policy change is a single edit. The
12
+ * registry is prompt-agnostic: it owns WHICH CLI runs and HOW its argv is
13
+ * built ({ argv, outMode }), not WHAT to ask. A skill keeps its own role
14
+ * table (prompts + per-role `preferenceOrder` + timeouts) and passes the
15
+ * order into `resolveBackendForRole`. Detection uses `which` (cross-platform)
16
+ * rather than `command -v` under `shell: true`, which mangles on Windows. The
17
+ * resolver is pure — it returns a structured result (chosen backend + why)
18
+ * and never logs, so the caller decides how to surface a fallback or a skip.
19
+ * `opencode` is hybrid (it dispatches to whatever provider its own config
20
+ * selects, e.g. Fireworks / Synthetic), so it is NEVER auto-picked from a
21
+ * preference order — only when a caller names it explicitly — to keep model
22
+ * attribution accurate.
23
+ */
24
+ /**
25
+ * A CLI backend the fleet can delegate a pass to.
26
+ */
27
+ export type BackendName = 'claude' | 'codex' | 'kimi' | 'opencode';
28
+ /**
29
+ * How a backend's process emits its result.
30
+ */
31
+ export type BackendOutMode = 'file' | 'stdout';
32
+ export interface BackendRun {
33
+ readonly argv: readonly string[];
34
+ readonly outMode: BackendOutMode;
35
+ }
36
+ export interface BackendDescriptor {
37
+ readonly bin: string;
38
+ readonly hybrid: boolean;
39
+ readonly name: BackendName;
40
+ readonly run: (promptFile: string, outFile: string) => BackendRun;
41
+ }
42
+ export declare const BACKENDS: Readonly<Record<BackendName, BackendDescriptor>>;
43
+ /**
44
+ * The set of backends whose CLI is installed. Fans the `which` lookups out
45
+ * concurrently rather than awaiting one at a time.
46
+ */
47
+ export declare function detectAvailableBackends(): Promise<ReadonlySet<BackendName>>;
48
+ /**
49
+ * True when `value` names a known backend.
50
+ */
51
+ export declare function isBackendName(value: string): value is BackendName;
52
+ /**
53
+ * True when the named CLI resolves on PATH (cross-platform via `which`).
54
+ */
55
+ export declare function isCommandAvailable(bin: string): Promise<boolean>;
56
+ export type BackendResolution = {
57
+ readonly backend: BackendName;
58
+ readonly reason: 'override';
59
+ } | {
60
+ readonly backend: BackendName;
61
+ readonly reason: 'preference';
62
+ } | {
63
+ readonly backend: BackendName;
64
+ readonly reason: 'preference';
65
+ readonly overrideMissing: BackendName;
66
+ } | {
67
+ readonly backend: undefined;
68
+ readonly reason: 'unavailable';
69
+ };
70
+ export interface ResolveBackendOptions {
71
+ readonly preferenceOrder: readonly BackendName[];
72
+ readonly available: ReadonlySet<BackendName>;
73
+ readonly override?: BackendName | undefined;
74
+ }
75
+ /**
76
+ * Resolve which backend runs a pass, encoding the fleet detection policy
77
+ * (`_shared/multi-agent-backends.md`): an installed explicit override wins;
78
+ * else the first installed non-hybrid entry in the preference order; else
79
+ * nothing (skip the pass). Pure — returns the decision + why, never logs. An
80
+ * override that isn't installed is reported via `overrideMissing` so the caller
81
+ * can warn.
82
+ */
83
+ export declare function resolveBackendForRole(options: ResolveBackendOptions): BackendResolution;
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with rolldown */
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_bin_which = require('../bin/which.js');
5
+ const require_ai_spawn = require('./spawn.js');
6
+
7
+ //#region src/ai/backends.mts
8
+ /**
9
+ * @file Multi-agent CLI backend registry + role routing. The fleet's review /
10
+ * scan / fix skills delegate work to whichever agent CLIs are installed
11
+ * (`codex`, `claude`, `kimi`, `opencode`), falling back through a per-role
12
+ * preference order and skipping a pass when nothing usable is present. Before
13
+ * this module each skill copied the same registry + detection block inline
14
+ * (the canonical copy lived in
15
+ * `.claude/skills/fleet/reviewing-code/run.mts`); the shared policy doc
16
+ * (`_shared/multi-agent-backends.md`) flagged the extraction. Import
17
+ * `BACKENDS` / `detectAvailableBackends` / `resolveBackendForRole` here
18
+ * instead so a new backend or a routing-policy change is a single edit. The
19
+ * registry is prompt-agnostic: it owns WHICH CLI runs and HOW its argv is
20
+ * built ({ argv, outMode }), not WHAT to ask. A skill keeps its own role
21
+ * table (prompts + per-role `preferenceOrder` + timeouts) and passes the
22
+ * order into `resolveBackendForRole`. Detection uses `which` (cross-platform)
23
+ * rather than `command -v` under `shell: true`, which mangles on Windows. The
24
+ * resolver is pure — it returns a structured result (chosen backend + why)
25
+ * and never logs, so the caller decides how to surface a fallback or a skip.
26
+ * `opencode` is hybrid (it dispatches to whatever provider its own config
27
+ * selects, e.g. Fireworks / Synthetic), so it is NEVER auto-picked from a
28
+ * preference order — only when a caller names it explicitly — to keep model
29
+ * attribution accurate.
30
+ */
31
+ const BACKENDS = {
32
+ __proto__: null,
33
+ claude: {
34
+ bin: "claude",
35
+ hybrid: false,
36
+ name: "claude",
37
+ run(_promptFile, _outFile) {
38
+ const model = process.env["CLAUDE_MODEL"] ?? "opus";
39
+ const effort = process.env["CLAUDE_EFFORT"] ?? "high";
40
+ return {
41
+ argv: [
42
+ "--print",
43
+ "--model",
44
+ model,
45
+ ...require_ai_spawn.isAdaptiveOnlyModel(model) ? [] : ["--effort", effort],
46
+ "--no-session-persistence",
47
+ "--permission-mode",
48
+ "dontAsk"
49
+ ],
50
+ outMode: "stdout"
51
+ };
52
+ }
53
+ },
54
+ codex: {
55
+ bin: "codex",
56
+ hybrid: false,
57
+ name: "codex",
58
+ run(_promptFile, outFile) {
59
+ return {
60
+ argv: [
61
+ "exec",
62
+ "--model",
63
+ process.env["CODEX_MODEL"] ?? "gpt-5.5",
64
+ "-c",
65
+ `model_reasoning_effort=${process.env["CODEX_REASONING"] ?? "xhigh"}`,
66
+ "--full-auto",
67
+ "--ephemeral",
68
+ "-o",
69
+ outFile,
70
+ "-"
71
+ ],
72
+ outMode: "file"
73
+ };
74
+ }
75
+ },
76
+ kimi: {
77
+ bin: "kimi",
78
+ hybrid: false,
79
+ name: "kimi",
80
+ run(_promptFile, _outFile) {
81
+ return {
82
+ argv: [
83
+ "chat",
84
+ "--model",
85
+ process.env["KIMI_MODEL"] ?? "kimi-latest",
86
+ "--no-stream"
87
+ ],
88
+ outMode: "stdout"
89
+ };
90
+ }
91
+ },
92
+ opencode: {
93
+ bin: "opencode",
94
+ hybrid: true,
95
+ name: "opencode",
96
+ run(_promptFile, _outFile) {
97
+ const model = process.env["OPENCODE_MODEL"];
98
+ return {
99
+ argv: model ? [
100
+ "run",
101
+ "--model",
102
+ model
103
+ ] : ["run"],
104
+ outMode: "stdout"
105
+ };
106
+ }
107
+ }
108
+ };
109
+ /**
110
+ * The set of backends whose CLI is installed. Fans the `which` lookups out
111
+ * concurrently rather than awaiting one at a time.
112
+ */
113
+ async function detectAvailableBackends() {
114
+ const names = Object.keys(BACKENDS);
115
+ const results = await Promise.all(names.map(async (name) => ({
116
+ available: await isCommandAvailable(BACKENDS[name].bin),
117
+ name
118
+ })));
119
+ return new Set(results.filter((r) => r.available).map((r) => r.name));
120
+ }
121
+ /**
122
+ * True when `value` names a known backend.
123
+ */
124
+ function isBackendName(value) {
125
+ return value in BACKENDS;
126
+ }
127
+ /**
128
+ * True when the named CLI resolves on PATH (cross-platform via `which`).
129
+ */
130
+ async function isCommandAvailable(bin) {
131
+ return await require_bin_which.which(bin) !== null;
132
+ }
133
+ /**
134
+ * Resolve which backend runs a pass, encoding the fleet detection policy
135
+ * (`_shared/multi-agent-backends.md`): an installed explicit override wins;
136
+ * else the first installed non-hybrid entry in the preference order; else
137
+ * nothing (skip the pass). Pure — returns the decision + why, never logs. An
138
+ * override that isn't installed is reported via `overrideMissing` so the caller
139
+ * can warn.
140
+ */
141
+ function resolveBackendForRole(options) {
142
+ const { available, override, preferenceOrder } = {
143
+ __proto__: null,
144
+ ...options
145
+ };
146
+ if (override && available.has(override)) return {
147
+ backend: override,
148
+ reason: "override"
149
+ };
150
+ for (let i = 0, { length } = preferenceOrder; i < length; i += 1) {
151
+ const candidate = preferenceOrder[i];
152
+ if (BACKENDS[candidate]?.hybrid) continue;
153
+ if (available.has(candidate)) return override ? {
154
+ backend: candidate,
155
+ overrideMissing: override,
156
+ reason: "preference"
157
+ } : {
158
+ backend: candidate,
159
+ reason: "preference"
160
+ };
161
+ }
162
+ return {
163
+ backend: void 0,
164
+ reason: "unavailable"
165
+ };
166
+ }
167
+
168
+ //#endregion
169
+ exports.BACKENDS = BACKENDS;
170
+ exports.detectAvailableBackends = detectAvailableBackends;
171
+ exports.isBackendName = isBackendName;
172
+ exports.isCommandAvailable = isCommandAvailable;
173
+ exports.resolveBackendForRole = resolveBackendForRole;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * @file Layered provider-credential resolver for AI backends. One call site,
3
+ * dev and CI: a provider token resolves from an explicit override, then the
4
+ * provider's env var, then the OS keychain — mirroring the
5
+ * `readSocketApiToken` env → keychain precedence
6
+ * (`secrets/socket-api-token.ts`). Why a single resolver: `ai/http.mts` read
7
+ * `process.env[tokenEnv]` inline, so every consumer hard-coded the env-only
8
+ * path and none could reach the keychain. Routing skills also need a uniform
9
+ * way to ask "do I have a credential for provider X?" without knowing its
10
+ * env-var name. This centralizes the provider → { tokenEnv, keychainService }
11
+ * map (the HTTP providers reuse `AI_HTTP_PROVIDERS` so the env var isn't
12
+ * duplicated) and the precedence. CI vs dev: pass `allowEnvOnly: true` (the
13
+ * resolver's existing escape) in headless contexts so a missing token returns
14
+ * `undefined` immediately instead of triggering a keychain auth prompt. CI
15
+ * sets the token as a GH-secret env var (e.g. `ANTHROPIC_API_KEY`); the same
16
+ * `resolveProviderCredential` call reads it there with no keychain. proteus
17
+ * hook-point: the forthcoming biometric credential daemon
18
+ * (`.claude/plans/proteus-credential-broker.md`) slots in as a layer between
19
+ * the env check and the keychain read inside `resolve()`'s implementation —
20
+ * call sites here do not change when it lands (resolver decision #4 in that
21
+ * plan). This module is the stable seam.
22
+ */
23
+ /**
24
+ * A provider whose credential this module can resolve: the HTTP providers
25
+ * (fireworks, synthetic) plus the CLI/first-party providers (anthropic, openai,
26
+ * xai) for CI env + keychain.
27
+ */
28
+ export type CredentialProvider = 'anthropic' | 'fireworks' | 'openai' | 'synthetic' | 'xai';
29
+ export interface ProviderCredentialSpec {
30
+ readonly tokenEnv: string;
31
+ readonly keychainService: string;
32
+ }
33
+ export declare const PROVIDER_CREDENTIALS: Readonly<Record<CredentialProvider, ProviderCredentialSpec>>;
34
+ /**
35
+ * True when `value` names a provider with a resolvable credential.
36
+ */
37
+ export declare function isCredentialProvider(value: string): value is CredentialProvider;
38
+ export interface ResolveProviderCredentialOptions {
39
+ readonly provider: CredentialProvider;
40
+ readonly explicit?: string | undefined;
41
+ readonly allowEnvOnly?: boolean | undefined;
42
+ }
43
+ /**
44
+ * Resolve a provider's bearer token: explicit override → env var → keychain →
45
+ * undefined. The token never appears inline or in logs — callers pass the
46
+ * result straight to an `Authorization` header. Returns `undefined` when no
47
+ * source has it (the caller decides whether that's fatal).
48
+ */
49
+ export declare function resolveProviderCredential(options: ResolveProviderCredentialOptions): Promise<string | undefined>;