@socketsecurity/lib 6.0.7 → 6.0.8

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 (263) hide show
  1. package/CHANGELOG.md +20 -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 +69 -0
  18. package/dist/ai/route.js +156 -0
  19. package/dist/ai/spawn.d.mts +10 -2
  20. package/dist/ai/spawn.js +55 -31
  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 +15 -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 +3915 -3781
  34. package/dist/bin/resolve.js +1 -1
  35. package/dist/cache/ttl/store.js +1 -1
  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 +6 -2
  41. package/dist/compression/gzip.js +6 -2
  42. package/dist/constants/packages.d.ts +3 -0
  43. package/dist/constants/packages.js +2 -1
  44. package/dist/constants/socket.d.ts +2 -6
  45. package/dist/constants/socket.js +12 -14
  46. package/dist/cover/code.js +2 -2
  47. package/dist/crypto/hash.d.ts +4 -1
  48. package/dist/crypto/hash.js +4 -1
  49. package/dist/debug/caller-info.js +1 -1
  50. package/dist/dlx/arborist.js +13 -3
  51. package/dist/dlx/binary-cache.js +1 -1
  52. package/dist/dlx/binary-resolution.js +1 -1
  53. package/dist/dlx/detect.d.ts +8 -0
  54. package/dist/dlx/firewall.d.ts +8 -0
  55. package/dist/dlx/firewall.js +1 -1
  56. package/dist/dlx/lockfile.js +4 -1
  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 +1 -1
  71. package/dist/env/proxy.js +1 -1
  72. package/dist/env/rewire.d.ts +1 -0
  73. package/dist/env/rewire.js +1 -1
  74. package/dist/env/socket.d.ts +7 -0
  75. package/dist/env/socket.js +10 -0
  76. package/dist/errors/predicates.js +1 -1
  77. package/dist/external/@npmcli/promise-spawn.js +3 -1
  78. package/dist/external/pico-pack.js +4 -2
  79. package/dist/external/which.js +3 -1
  80. package/dist/external-tools/bazel/asset-names.d.ts +1 -1
  81. package/dist/external-tools/bazel/asset-names.js +5 -2
  82. package/dist/external-tools/bazel/from-download.d.ts +1 -1
  83. package/dist/external-tools/bazel/from-download.js +5 -2
  84. package/dist/external-tools/bazel/resolve-bazel-version.js +4 -0
  85. package/dist/external-tools/bazel/resolve.d.ts +3 -3
  86. package/dist/external-tools/bazel/resolve.js +16 -8
  87. package/dist/external-tools/cdxgen/asset-names.d.ts +1 -1
  88. package/dist/external-tools/cdxgen/asset-names.js +5 -2
  89. package/dist/external-tools/cdxgen/from-download.d.ts +1 -1
  90. package/dist/external-tools/cdxgen/from-download.js +7 -4
  91. package/dist/external-tools/cdxgen/resolve.d.ts +3 -3
  92. package/dist/external-tools/cdxgen/resolve.js +16 -8
  93. package/dist/external-tools/from-download.d.ts +2 -2
  94. package/dist/external-tools/from-download.js +11 -5
  95. package/dist/external-tools/from-pip-venv.d.ts +1 -1
  96. package/dist/external-tools/from-pip-venv.js +12 -5
  97. package/dist/external-tools/janus/asset-names.d.ts +1 -1
  98. package/dist/external-tools/janus/asset-names.js +5 -2
  99. package/dist/external-tools/janus/from-download.d.ts +1 -1
  100. package/dist/external-tools/janus/from-download.js +5 -2
  101. package/dist/external-tools/janus/resolve.d.ts +3 -3
  102. package/dist/external-tools/janus/resolve.js +16 -8
  103. package/dist/external-tools/jre/asset-names.d.ts +1 -1
  104. package/dist/external-tools/jre/asset-names.js +5 -2
  105. package/dist/external-tools/jre/from-download.d.ts +1 -1
  106. package/dist/external-tools/jre/from-download.js +7 -4
  107. package/dist/external-tools/jre/from-java-home.js +2 -2
  108. package/dist/external-tools/jre/from-vfs.js +2 -2
  109. package/dist/external-tools/jre/resolve.d.ts +3 -3
  110. package/dist/external-tools/jre/resolve.js +16 -8
  111. package/dist/external-tools/manifest.d.ts +18 -0
  112. package/dist/external-tools/manifest.js +1 -1
  113. package/dist/external-tools/opengrep/asset-names.d.ts +1 -1
  114. package/dist/external-tools/opengrep/asset-names.js +5 -2
  115. package/dist/external-tools/opengrep/from-download.d.ts +1 -1
  116. package/dist/external-tools/opengrep/from-download.js +5 -2
  117. package/dist/external-tools/opengrep/resolve.d.ts +3 -3
  118. package/dist/external-tools/opengrep/resolve.js +16 -8
  119. package/dist/external-tools/python/asset-names.d.ts +1 -1
  120. package/dist/external-tools/python/asset-names.js +10 -3
  121. package/dist/external-tools/python/dlx.d.ts +3 -3
  122. package/dist/external-tools/python/dlx.js +20 -9
  123. package/dist/external-tools/python/from-download.d.ts +1 -1
  124. package/dist/external-tools/python/from-download.js +12 -5
  125. package/dist/external-tools/python/pin.js +6 -3
  126. package/dist/external-tools/python/pip-install.js +6 -3
  127. package/dist/external-tools/python/resolve.d.ts +3 -3
  128. package/dist/external-tools/python/resolve.js +19 -11
  129. package/dist/external-tools/sbt/asset-names.d.ts +1 -1
  130. package/dist/external-tools/sbt/asset-names.js +5 -2
  131. package/dist/external-tools/sbt/from-download.d.ts +1 -1
  132. package/dist/external-tools/sbt/from-download.js +5 -2
  133. package/dist/external-tools/sbt/resolve.d.ts +3 -3
  134. package/dist/external-tools/sbt/resolve.js +16 -8
  135. package/dist/external-tools/skillspector/from-dlx.d.ts +1 -1
  136. package/dist/external-tools/skillspector/from-dlx.js +10 -3
  137. package/dist/external-tools/skillspector/resolve.d.ts +2 -2
  138. package/dist/external-tools/skillspector/resolve.js +14 -6
  139. package/dist/external-tools/synp/asset-names.d.ts +1 -1
  140. package/dist/external-tools/synp/asset-names.js +6 -2
  141. package/dist/external-tools/synp/from-download.d.ts +1 -1
  142. package/dist/external-tools/synp/from-download.js +5 -2
  143. package/dist/external-tools/synp/resolve.d.ts +3 -3
  144. package/dist/external-tools/synp/resolve.js +16 -8
  145. package/dist/external-tools/trivy/asset-names.d.ts +1 -1
  146. package/dist/external-tools/trivy/asset-names.js +5 -2
  147. package/dist/external-tools/trivy/from-download.d.ts +1 -1
  148. package/dist/external-tools/trivy/from-download.js +7 -4
  149. package/dist/external-tools/trivy/resolve.d.ts +3 -3
  150. package/dist/external-tools/trivy/resolve.js +16 -8
  151. package/dist/external-tools/trufflehog/asset-names.d.ts +1 -1
  152. package/dist/external-tools/trufflehog/asset-names.js +5 -2
  153. package/dist/external-tools/trufflehog/from-download.d.ts +1 -1
  154. package/dist/external-tools/trufflehog/from-download.js +7 -4
  155. package/dist/external-tools/trufflehog/resolve.d.ts +3 -3
  156. package/dist/external-tools/trufflehog/resolve.js +16 -8
  157. package/dist/fs/allowed-dirs-cache.d.ts +27 -1
  158. package/dist/fs/allowed-dirs-cache.js +38 -3
  159. package/dist/fs/find.js +1 -1
  160. package/dist/fs/read-json-cache.d.ts +7 -0
  161. package/dist/fs/resolve-module.js +6 -2
  162. package/dist/fs/safe.js +1 -1
  163. package/dist/git/_internal.js +2 -2
  164. package/dist/git/repo.js +2 -4
  165. package/dist/git/staged.js +8 -0
  166. package/dist/git/tracked.d.ts +84 -0
  167. package/dist/git/tracked.js +163 -0
  168. package/dist/git/unstaged.js +8 -0
  169. package/dist/github/refs-graphql.js +4 -0
  170. package/dist/github/refs-rest.js +4 -0
  171. package/dist/github/refs.js +15 -10
  172. package/dist/globs/_internal.js +1 -1
  173. package/dist/globs/match.js +9 -1
  174. package/dist/globs/matcher.js +5 -1
  175. package/dist/http-request/browser.js +6 -2
  176. package/dist/http-request/{browser-fetch.d.ts → fetch/browser.d.ts} +2 -2
  177. package/dist/http-request/{browser-fetch.js → fetch/browser.js} +4 -4
  178. package/dist/http-request/headers.js +1 -1
  179. package/dist/http-request/request-attempt.js +2 -2
  180. package/dist/http-request/user-agent.js +1 -1
  181. package/dist/integrity.d.ts +10 -4
  182. package/dist/integrity.js +10 -4
  183. package/dist/json/edit.js +38 -30
  184. package/dist/json/format.js +1 -1
  185. package/dist/native-messaging/install.d.ts +1 -1
  186. package/dist/native-messaging/install.js +7 -4
  187. package/dist/native-messaging/rate-limit.d.ts +7 -0
  188. package/dist/native-messaging/rate-limit.js +4 -0
  189. package/dist/node/async-hooks.js +1 -1
  190. package/dist/node/child-process.js +1 -1
  191. package/dist/node/crypto.js +1 -1
  192. package/dist/node/events.js +1 -1
  193. package/dist/node/fs-promises.js +1 -1
  194. package/dist/node/fs.d.ts +22 -6
  195. package/dist/node/fs.js +16 -3
  196. package/dist/node/http.js +1 -1
  197. package/dist/node/https.js +1 -1
  198. package/dist/node/module.js +1 -1
  199. package/dist/node/os.d.ts +10 -2
  200. package/dist/node/os.js +11 -4
  201. package/dist/node/path.d.ts +11 -2
  202. package/dist/node/path.js +17 -4
  203. package/dist/node/timers-promises.js +1 -1
  204. package/dist/node/url.js +1 -1
  205. package/dist/node/util.js +1 -1
  206. package/dist/objects/getters.js +1 -1
  207. package/dist/objects/mutate.js +2 -2
  208. package/dist/objects/predicates.js +1 -1
  209. package/dist/packages/edit-class.d.ts +2 -3
  210. package/dist/packages/edit-class.js +41 -35
  211. package/dist/packages/exports.js +4 -4
  212. package/dist/packages/fetch.js +1 -1
  213. package/dist/packages/isolation.js +1 -1
  214. package/dist/packages/licenses.js +2 -2
  215. package/dist/packages/manifest.js +4 -4
  216. package/dist/packages/normalize.js +1 -1
  217. package/dist/packages/provenance.js +2 -2
  218. package/dist/packages/specs.js +1 -1
  219. package/dist/packages/tarball.js +4 -2
  220. package/dist/packages/types.d.ts +1 -2
  221. package/dist/paths/dirnames.d.ts +1 -0
  222. package/dist/paths/dirnames.js +2 -0
  223. package/dist/paths/resolve.js +14 -19
  224. package/dist/paths/rewire.d.ts +5 -0
  225. package/dist/paths/socket.d.ts +74 -111
  226. package/dist/paths/socket.js +99 -132
  227. package/dist/primordials/process.d.ts +88 -0
  228. package/dist/primordials/process.js +132 -0
  229. package/dist/primordials/uncurry.d.ts +1 -2
  230. package/dist/process/spawn/child.js +8 -2
  231. package/dist/process/spawn/errors.js +1 -1
  232. package/dist/regexps/spec.js +1 -1
  233. package/dist/releases/github-archives.js +1 -1
  234. package/dist/releases/github-listing.d.ts +1 -2
  235. package/dist/schema/types.d.ts +3 -4
  236. package/dist/schema/validate.js +1 -1
  237. package/dist/secrets/find.d.ts +2 -2
  238. package/dist/secrets/find.js +10 -4
  239. package/dist/secrets/keychain.d.ts +1 -1
  240. package/dist/secrets/linux.js +32 -44
  241. package/dist/secrets/macos.d.ts +1 -2
  242. package/dist/secrets/macos.js +20 -29
  243. package/dist/secrets/rc.d.ts +2 -2
  244. package/dist/secrets/rc.js +21 -13
  245. package/dist/secrets/socket-api-token.js +8 -0
  246. package/dist/secrets/windows.js +27 -33
  247. package/dist/shell/parse.d.ts +32 -0
  248. package/dist/shell/parse.js +60 -0
  249. package/dist/spinner/create-spinner-class.js +2 -2
  250. package/dist/spinner/spinner-internals.d.ts +1 -1
  251. package/dist/spinner/spinner-internals.js +9 -5
  252. package/dist/spinner/spinner.d.ts +4 -0
  253. package/dist/spinner/spinner.js +1 -1
  254. package/dist/stdio/progress.js +5 -1
  255. package/dist/stdio/prompts.d.ts +2 -2
  256. package/dist/stdio/prompts.js +1 -1
  257. package/dist/temporal/instant.js +2 -2
  258. package/dist/url/assert-safe.d.ts +29 -0
  259. package/dist/url/assert-safe.js +54 -0
  260. package/dist/url/predicates.d.ts +31 -1
  261. package/dist/url/predicates.js +42 -1
  262. package/dist/url/types.d.ts +4 -0
  263. package/package.json +177 -115
package/CHANGELOG.md CHANGED
@@ -5,6 +5,26 @@ 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.8](https://github.com/SocketDev/socket-lib/releases/tag/v6.0.8) - 2026-06-11
9
+
10
+ ### Added
11
+
12
+ - **`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.
13
+ - **`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.
14
+ - **`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).
15
+ - **`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.
16
+ - **`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.
17
+
18
+ ### Changed
19
+
20
+ - **`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.
21
+ - **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.
22
+ - **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.
23
+
24
+ ### Fixed
25
+
26
+ - **`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.
27
+
8
28
  ## [6.0.7](https://github.com/SocketDev/socket-lib/releases/tag/v6.0.7) - 2026-06-03
9
29
 
10
30
  ### 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>;