@socketsecurity/lib 6.0.9 → 6.0.10

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 (335) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +8 -9
  3. package/dist/abort/signal.d.ts +1 -1
  4. package/dist/abort/signal.js +1 -1
  5. package/dist/ai/agent-context.d.mts +1 -1
  6. package/dist/ai/agent-context.js +3 -2
  7. package/dist/ai/backends.d.mts +3 -3
  8. package/dist/ai/backends.js +2 -2
  9. package/dist/ai/billing-context.d.mts +74 -0
  10. package/dist/ai/billing-context.js +129 -0
  11. package/dist/ai/credentials.d.mts +39 -5
  12. package/dist/ai/credentials.js +61 -6
  13. package/dist/ai/discover.d.mts +4 -0
  14. package/dist/ai/discover.js +4 -0
  15. package/dist/ai/exec.d.mts +1 -1
  16. package/dist/ai/exec.js +1 -1
  17. package/dist/ai/http.d.mts +8 -8
  18. package/dist/ai/http.js +6 -6
  19. package/dist/ai/profiles.d.mts +9 -10
  20. package/dist/ai/profiles.js +0 -5
  21. package/dist/ai/role.d.mts +58 -0
  22. package/dist/ai/role.js +96 -0
  23. package/dist/ai/route-heuristic.d.mts +177 -0
  24. package/dist/ai/route-heuristic.js +194 -0
  25. package/dist/ai/route.d.mts +10 -9
  26. package/dist/ai/route.js +14 -13
  27. package/dist/ai/spawn.d.mts +2 -1
  28. package/dist/ai/spawn.js +25 -2
  29. package/dist/ai/subagent-status.d.mts +2 -0
  30. package/dist/ai/subagent-status.js +2 -0
  31. package/dist/ai/tier.d.mts +1 -1
  32. package/dist/ai/types.d.mts +9 -2
  33. package/dist/ai/worktree.d.mts +1 -1
  34. package/dist/ai/worktree.js +3 -2
  35. package/dist/argv/parse.d.ts +2 -0
  36. package/dist/argv/parse.js +2 -0
  37. package/dist/bin/_internal.d.ts +1 -1
  38. package/dist/bin/_internal.js +1 -1
  39. package/dist/bin/prim.cjs +446 -300
  40. package/dist/bin/shadow.js +2 -1
  41. package/dist/bin/which.d.ts +28 -3
  42. package/dist/bin/which.js +45 -3
  43. package/dist/cacache/_internal.d.ts +10 -2
  44. package/dist/cacache/_internal.js +6 -10
  45. package/dist/cache/ttl/_internal.d.ts +33 -0
  46. package/dist/cache/ttl/_internal.js +68 -0
  47. package/dist/cache/ttl/browser.d.ts +56 -0
  48. package/dist/cache/ttl/browser.js +257 -0
  49. package/dist/cache/ttl/store.js +10 -33
  50. package/dist/cache/ttl/types.d.ts +46 -3
  51. package/dist/checks/primordials-defaults.d.ts +5 -5
  52. package/dist/checks/primordials-defaults.js +5 -5
  53. package/dist/checks/primordials.d.ts +1 -1
  54. package/dist/checks/primordials.js +25 -17
  55. package/dist/cli/check-primordials.d.ts +6 -1
  56. package/dist/cli/check-primordials.js +14 -5
  57. package/dist/cli/check.js +1 -1
  58. package/dist/cli/socket-lib.d.ts +1 -1
  59. package/dist/cli/socket-lib.js +3 -3
  60. package/dist/colors/socket-palette.d.ts +1 -1
  61. package/dist/compression/brotli.d.ts +6 -2
  62. package/dist/compression/brotli.js +4 -0
  63. package/dist/compression/gzip.d.ts +6 -2
  64. package/dist/compression/gzip.js +6 -1
  65. package/dist/config/layers.d.ts +4 -4
  66. package/dist/config/layers.js +4 -4
  67. package/dist/constants/agents.d.ts +3 -0
  68. package/dist/constants/agents.js +3 -0
  69. package/dist/constants/node.d.ts +14 -0
  70. package/dist/constants/node.js +14 -0
  71. package/dist/constants/package-default-node-range.js +1 -5
  72. package/dist/constants/packages.d.ts +5 -0
  73. package/dist/constants/packages.js +5 -0
  74. package/dist/constants/platform.d.ts +1 -1
  75. package/dist/constants/platform.js +1 -1
  76. package/dist/constants/socket.js +1 -1
  77. package/dist/constants/typescript.d.ts +5 -0
  78. package/dist/constants/typescript.js +5 -0
  79. package/dist/debug/_internal.d.ts +22 -7
  80. package/dist/debug/_internal.js +33 -15
  81. package/dist/debug/namespace.js +11 -8
  82. package/dist/debug/output.d.ts +12 -1
  83. package/dist/debug/output.js +33 -16
  84. package/dist/dlx/binary-resolution.js +7 -4
  85. package/dist/eco/npm/npm/parse-lockfile.js +2 -1
  86. package/dist/eco/npm/vlt/exec.d.ts +1 -1
  87. package/dist/eco/npm/vlt/exec.js +1 -1
  88. package/dist/env/agents.d.ts +6 -0
  89. package/dist/env/agents.js +29 -0
  90. package/dist/env/flags.d.ts +12 -0
  91. package/dist/env/flags.js +23 -0
  92. package/dist/env/github-status.js +2 -2
  93. package/dist/env/providers.d.ts +6 -0
  94. package/dist/env/providers.js +23 -0
  95. package/dist/env/proxy.js +1 -1
  96. package/dist/env/rewire.d.ts +32 -0
  97. package/dist/env/rewire.js +55 -23
  98. package/dist/env/runtimes.d.ts +9 -0
  99. package/dist/env/runtimes.js +53 -0
  100. package/dist/env/socket-cli.d.ts +5 -2
  101. package/dist/env/socket-cli.js +6 -3
  102. package/dist/errors/predicates.d.ts +3 -2
  103. package/dist/errors/predicates.js +5 -3
  104. package/dist/errors/stack.js +2 -1
  105. package/dist/events/exit/_internal.d.ts +1 -23
  106. package/dist/external/@npmcli/package-json.js +179 -88
  107. package/dist/external/@npmcli/promise-spawn.js +2 -2
  108. package/dist/external/@sinclair/typebox/value.js +5 -4
  109. package/dist/external/@socketregistry/yocto-spinner.js +3 -3
  110. package/dist/external/adm-zip.d.ts +82 -0
  111. package/dist/external/adm-zip.js +2 -2
  112. package/dist/external/cacache.d.ts +87 -0
  113. package/dist/external/debug.d.ts +22 -0
  114. package/dist/external/debug.js +11 -4
  115. package/dist/external/del.d.ts +1 -0
  116. package/dist/external/external-pack.d.ts +12 -0
  117. package/dist/external/external-pack.js +41 -34
  118. package/dist/external/fast-glob.d.ts +8 -0
  119. package/dist/external/fast-sort.d.ts +1 -0
  120. package/dist/external/get-east-asian-width.d.ts +5 -0
  121. package/dist/external/get-east-asian-width.js +8 -1
  122. package/dist/external/has-flag.d.ts +1 -0
  123. package/dist/external/inquirer-pack.d.ts +18 -0
  124. package/dist/external/libnpmexec.d.ts +35 -0
  125. package/dist/external/libnpmpack.d.ts +1 -0
  126. package/dist/external/make-fetch-happen.d.ts +13 -0
  127. package/dist/external/normalize-package-data.d.ts +4 -0
  128. package/dist/external/npm-core.d.ts +14 -0
  129. package/dist/external/npm-pack.d.ts +24 -0
  130. package/dist/external/npm-pack.js +1551 -1867
  131. package/dist/external/npm-package-arg.d.ts +4 -0
  132. package/dist/external/p-map.js +8 -1
  133. package/dist/external/pacote.d.ts +15 -0
  134. package/dist/external/pico-pack.d.ts +16 -0
  135. package/dist/external/pico-pack.js +38 -23
  136. package/dist/external/picomatch.d.ts +96 -0
  137. package/dist/external/pony-cause.d.ts +14 -0
  138. package/dist/external/semver.d.ts +60 -0
  139. package/dist/external/semver.js +1459 -4
  140. package/dist/external/shell-quote.d.ts +51 -0
  141. package/dist/external/shell-quote.js +55 -15
  142. package/dist/external/signal-exit.d.ts +1 -0
  143. package/dist/external/spdx-correct.d.ts +4 -0
  144. package/dist/external/spdx-expression-parse.d.ts +4 -0
  145. package/dist/external/spdx-pack.d.ts +10 -0
  146. package/dist/external/std-env.d.ts +284 -0
  147. package/dist/external/std-env.js +323 -0
  148. package/dist/external/streaming-iterables.d.ts +11 -0
  149. package/dist/external/supports-color.d.ts +1 -0
  150. package/dist/external/tar-fs.d.ts +31 -0
  151. package/dist/external/validate-npm-package-name.d.ts +4 -0
  152. package/dist/external/which.d.ts +25 -0
  153. package/dist/external/which.js +2 -2
  154. package/dist/external/yargs-parser.d.ts +2 -0
  155. package/dist/external/yargs-parser.js +11 -3
  156. package/dist/external/yoctocolors-cjs.d.ts +51 -0
  157. package/dist/external-tools/bazel/asset-names.d.ts +1 -1
  158. package/dist/external-tools/bazel/from-download.d.ts +1 -1
  159. package/dist/external-tools/bazel/resolve.d.ts +3 -0
  160. package/dist/external-tools/bazel/resolve.js +3 -0
  161. package/dist/external-tools/cdxgen/asset-names.d.ts +5 -5
  162. package/dist/external-tools/cdxgen/asset-names.js +3 -3
  163. package/dist/external-tools/cdxgen/from-download.d.ts +2 -2
  164. package/dist/external-tools/cdxgen/from-download.js +1 -1
  165. package/dist/external-tools/cdxgen/resolve.d.ts +4 -1
  166. package/dist/external-tools/cdxgen/resolve.js +4 -1
  167. package/dist/external-tools/cdxgen/types.d.ts +1 -1
  168. package/dist/external-tools/from-download.d.ts +2 -2
  169. package/dist/external-tools/from-download.js +1 -1
  170. package/dist/external-tools/janus/asset-names.d.ts +4 -1
  171. package/dist/external-tools/janus/asset-names.js +3 -0
  172. package/dist/external-tools/janus/from-download.d.ts +3 -3
  173. package/dist/external-tools/janus/from-download.js +2 -2
  174. package/dist/external-tools/janus/resolve.d.ts +3 -0
  175. package/dist/external-tools/janus/resolve.js +3 -0
  176. package/dist/external-tools/jre/asset-names.d.ts +3 -1
  177. package/dist/external-tools/jre/asset-names.js +2 -0
  178. package/dist/external-tools/jre/from-download.d.ts +1 -1
  179. package/dist/external-tools/manifest.d.ts +5 -3
  180. package/dist/external-tools/manifest.js +4 -2
  181. package/dist/external-tools/opengrep/asset-names.d.ts +1 -1
  182. package/dist/external-tools/opengrep/resolve.d.ts +3 -0
  183. package/dist/external-tools/opengrep/resolve.js +3 -0
  184. package/dist/external-tools/python/asset-names.d.ts +3 -1
  185. package/dist/external-tools/python/asset-names.js +7 -3
  186. package/dist/external-tools/python/pin.js +3 -1
  187. package/dist/external-tools/python/resolve.d.ts +3 -0
  188. package/dist/external-tools/python/resolve.js +3 -0
  189. package/dist/external-tools/sbt/resolve.d.ts +3 -0
  190. package/dist/external-tools/sbt/resolve.js +3 -0
  191. package/dist/external-tools/skillspector/from-uv.d.ts +1 -1
  192. package/dist/external-tools/synp/resolve.d.ts +3 -0
  193. package/dist/external-tools/synp/resolve.js +3 -0
  194. package/dist/external-tools/trivy/asset-names.d.ts +1 -1
  195. package/dist/external-tools/trivy/resolve.d.ts +3 -0
  196. package/dist/external-tools/trivy/resolve.js +3 -0
  197. package/dist/external-tools/trufflehog/asset-names.d.ts +4 -1
  198. package/dist/external-tools/trufflehog/asset-names.js +3 -0
  199. package/dist/external-tools/trufflehog/from-download.d.ts +1 -1
  200. package/dist/external-tools/trufflehog/resolve.d.ts +3 -0
  201. package/dist/external-tools/trufflehog/resolve.js +3 -0
  202. package/dist/external-tools/uv/asset-names.d.ts +1 -1
  203. package/dist/external-tools/uv/resolve.d.ts +3 -0
  204. package/dist/external-tools/uv/resolve.js +3 -0
  205. package/dist/fleet/repo-config.d.ts +8 -6
  206. package/dist/fleet/repo-config.js +8 -6
  207. package/dist/fs/_internal.d.ts +1 -1
  208. package/dist/fs/_internal.js +1 -1
  209. package/dist/fs/access.d.ts +2 -0
  210. package/dist/fs/access.js +2 -0
  211. package/dist/fs/allowed-dirs-cache.js +5 -3
  212. package/dist/fs/find.js +1 -2
  213. package/dist/fs/inspect.d.ts +2 -2
  214. package/dist/fs/read-file.js +3 -4
  215. package/dist/fs/safe.js +1 -1
  216. package/dist/git/_internal.js +5 -1
  217. package/dist/github/commit.d.ts +92 -0
  218. package/dist/github/commit.js +113 -0
  219. package/dist/github/ghsa.js +1 -1
  220. package/dist/github/refs-graphql.js +1 -1
  221. package/dist/github/request.js +1 -1
  222. package/dist/github/token.d.ts +5 -2
  223. package/dist/github/token.js +6 -4
  224. package/dist/github/workflow-runs.d.ts +107 -0
  225. package/dist/github/workflow-runs.js +98 -0
  226. package/dist/http-request/_internal.d.ts +1 -1
  227. package/dist/http-request/browser.d.ts +6 -0
  228. package/dist/http-request/browser.js +3 -2
  229. package/dist/http-request/download.js +2 -0
  230. package/dist/http-request/headers.d.ts +1 -1
  231. package/dist/http-request/headers.js +1 -1
  232. package/dist/http-request/request-attempt.js +6 -3
  233. package/dist/http-request/request-types.d.ts +10 -1
  234. package/dist/http-request/request.js +2 -2
  235. package/dist/http-request/user-agent.d.ts +23 -2
  236. package/dist/http-request/user-agent.js +48 -6
  237. package/dist/integrity.d.ts +3 -1
  238. package/dist/integrity.js +3 -1
  239. package/dist/json/format.js +4 -1
  240. package/dist/logger/_internal.d.ts +13 -15
  241. package/dist/logger/_internal.js +21 -21
  242. package/dist/logger/colors.d.ts +9 -3
  243. package/dist/logger/colors.js +6 -13
  244. package/dist/logger/console-methods.js +2 -2
  245. package/dist/logger/console.d.ts +1 -0
  246. package/dist/logger/console.js +9 -9
  247. package/dist/logger/node.js +2 -1
  248. package/dist/native-messaging/install.js +1 -1
  249. package/dist/native-messaging/rate-limit.d.ts +4 -1
  250. package/dist/native-messaging/rate-limit.js +4 -1
  251. package/dist/node/events.d.ts +3 -0
  252. package/dist/node/events.js +3 -0
  253. package/dist/node/fs.d.ts +1 -1
  254. package/dist/node/timers-promises.d.ts +3 -0
  255. package/dist/node/timers-promises.js +3 -0
  256. package/dist/node/url.d.ts +3 -0
  257. package/dist/node/url.js +3 -0
  258. package/dist/npm/meta-cache.d.ts +183 -0
  259. package/dist/npm/meta-cache.js +318 -0
  260. package/dist/npm/meta-slice.d.ts +27 -0
  261. package/dist/npm/meta-slice.js +68 -0
  262. package/dist/npm/meta-types.d.ts +282 -0
  263. package/dist/npm/meta-types.js +2 -0
  264. package/dist/npm/meta.d.ts +125 -0
  265. package/dist/npm/meta.js +347 -0
  266. package/dist/npm/registry.d.ts +212 -0
  267. package/dist/npm/registry.js +284 -0
  268. package/dist/objects/getters.d.ts +1 -1
  269. package/dist/objects/getters.js +1 -1
  270. package/dist/packages/manifest.js +6 -7
  271. package/dist/packages/metadata-extensions.js +2 -4
  272. package/dist/packages/provenance.d.ts +8 -0
  273. package/dist/packages/provenance.js +8 -0
  274. package/dist/packages/tarball.js +1 -2
  275. package/dist/packages/validation.d.ts +0 -10
  276. package/dist/packages/validation.js +0 -14
  277. package/dist/paths/exts.d.ts +30 -0
  278. package/dist/paths/exts.js +30 -0
  279. package/dist/paths/filenames.d.ts +6 -0
  280. package/dist/paths/filenames.js +6 -0
  281. package/dist/paths/globs.d.ts +6 -0
  282. package/dist/paths/globs.js +6 -0
  283. package/dist/paths/resolve.js +19 -14
  284. package/dist/paths/socket.d.ts +8 -8
  285. package/dist/paths/socket.js +8 -8
  286. package/dist/perf/report.js +4 -2
  287. package/dist/pkg-ext/data.js +0 -2
  288. package/dist/primordials/globals.d.ts +1 -1
  289. package/dist/primordials/globals.js +1 -1
  290. package/dist/primordials/headers.d.ts +13 -1
  291. package/dist/primordials/headers.js +13 -1
  292. package/dist/primordials/process.d.ts +1 -1
  293. package/dist/primordials/process.js +1 -1
  294. package/dist/primordials/url.d.ts +18 -0
  295. package/dist/primordials/url.js +18 -0
  296. package/dist/process/spawn/child.js +3 -5
  297. package/dist/promises/_internal.d.ts +1 -1
  298. package/dist/promises/_internal.js +1 -2
  299. package/dist/promises/options.js +3 -3
  300. package/dist/releases/socket-btm-binary-naming.js +12 -1
  301. package/dist/secrets/_internal.d.ts +1 -1
  302. package/dist/secrets/_internal.js +1 -1
  303. package/dist/secrets/find.d.ts +2 -2
  304. package/dist/secrets/find.js +2 -2
  305. package/dist/secrets/keychain.d.ts +10 -1
  306. package/dist/secrets/keychain.js +10 -1
  307. package/dist/secrets/socket-api-token.d.ts +1 -1
  308. package/dist/secrets/socket-api-token.js +1 -1
  309. package/dist/secrets/types.d.ts +1 -1
  310. package/dist/smol/http.d.ts +2 -0
  311. package/dist/smol/http.js +2 -0
  312. package/dist/smol/https.d.ts +2 -0
  313. package/dist/smol/https.js +2 -0
  314. package/dist/sorts/_internal.d.ts +2 -3
  315. package/dist/sorts/_internal.js +1 -3
  316. package/dist/spinner/format.js +2 -4
  317. package/dist/stdio/footer.js +8 -7
  318. package/dist/stdio/prompts.d.ts +8 -15
  319. package/dist/stdio/prompts.js +18 -29
  320. package/dist/strings/predicates.d.ts +2 -2
  321. package/dist/strings/search.js +1 -1
  322. package/dist/strings/types.d.ts +4 -0
  323. package/dist/strings/width.d.ts +1 -0
  324. package/dist/strings/width.js +17 -8
  325. package/dist/themes/context.d.ts +13 -0
  326. package/dist/themes/context.js +23 -8
  327. package/dist/versions/_internal.d.ts +43 -13
  328. package/dist/versions/_internal.js +27 -15
  329. package/dist/versions/compare.d.ts +16 -10
  330. package/dist/versions/compare.js +36 -16
  331. package/dist/versions/parse.js +1 -1
  332. package/dist/versions/range.js +3 -2
  333. package/dist/words/pluralize.js +4 -1
  334. package/llms.txt +63 -750
  335. package/package.json +200 -35
@@ -11,11 +11,10 @@
11
11
  * WebSearch. No Edit, no Write, no Bash. Static-analysis skills
12
12
  * (scanning-quality, scanning-security).
13
13
  * - `AI_PROFILE.edit` — in-place edits only. Read + Edit + Grep + Glob. NO
14
- * Write (can't create files), NO MultiEdit, NO Bash. Lint autofix /
15
- * codemods constrained to existing files.
16
- * - `AI_PROFILE.create` — edit AND create files. Adds MultiEdit + Write on top
17
- * of `.edit`. Still no Bash. Codegen, adding a test, refactors that split
18
- * modules.
14
+ * Write (can't create files), NO Bash. Lint autofix / codemods constrained
15
+ * to existing files.
16
+ * - `AI_PROFILE.create` — edit AND create files. Adds Write on top of `.edit`.
17
+ * Still no Bash. Codegen, adding a test, refactors that split modules.
19
18
  * - `AI_PROFILE.verify` — `.create` plus a READ-ONLY Bash allowlist (node /
20
19
  * pnpm test+run / git status·diff·log). Lets an agent author files AND run
21
20
  * the verifier (its own tests, a check script) — but it CANNOT mutate the
@@ -66,13 +65,13 @@ export declare const BASH_ALLOW: {
66
65
  export declare const AI_PROFILE: {
67
66
  readonly read: {
68
67
  readonly allow: readonly [];
69
- readonly disallow: readonly ["Agent", "Bash", "Edit", "MultiEdit", "Write"];
68
+ readonly disallow: readonly ["Agent", "Bash", "Edit", "Write"];
70
69
  readonly permissionMode: 'dontAsk';
71
70
  readonly tools: readonly ["Glob", "Grep", "Read", "WebFetch", "WebSearch"];
72
71
  };
73
72
  readonly edit: {
74
73
  readonly allow: readonly [];
75
- readonly disallow: readonly ["Agent", "Bash", "MultiEdit", "WebFetch", "WebSearch", "Write"];
74
+ readonly disallow: readonly ["Agent", "Bash", "WebFetch", "WebSearch", "Write"];
76
75
  readonly permissionMode: 'acceptEdits';
77
76
  readonly tools: readonly ["Edit", "Glob", "Grep", "Read"];
78
77
  };
@@ -80,18 +79,18 @@ export declare const AI_PROFILE: {
80
79
  readonly allow: readonly [];
81
80
  readonly disallow: readonly ["Agent", "Bash", "WebFetch", "WebSearch"];
82
81
  readonly permissionMode: 'acceptEdits';
83
- readonly tools: readonly ["Edit", "Glob", "Grep", "MultiEdit", "Read", "Write"];
82
+ readonly tools: readonly ["Edit", "Glob", "Grep", "Read", "Write"];
84
83
  };
85
84
  readonly verify: {
86
85
  readonly allow: readonly ["Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(node:*)", "Bash(pnpm run:*)", "Bash(pnpm test:*)"];
87
86
  readonly disallow: readonly ["Agent", "WebFetch", "WebSearch"];
88
87
  readonly permissionMode: 'acceptEdits';
89
- readonly tools: readonly ["Bash", "Edit", "Glob", "Grep", "MultiEdit", "Read", "Write"];
88
+ readonly tools: readonly ["Bash", "Edit", "Glob", "Grep", "Read", "Write"];
90
89
  };
91
90
  readonly full: {
92
91
  readonly allow: readonly ["Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(node:*)", "Bash(pnpm run:*)", "Bash(pnpm test:*)", "Bash(git add:*)", "Bash(git commit:*)", "Bash(pnpm exec:*)"];
93
92
  readonly disallow: readonly ["Agent", "WebFetch", "WebSearch"];
94
93
  readonly permissionMode: 'acceptEdits';
95
- readonly tools: readonly ["Bash", "Edit", "Glob", "Grep", "MultiEdit", "Read", "Write"];
94
+ readonly tools: readonly ["Bash", "Edit", "Glob", "Grep", "Read", "Write"];
96
95
  };
97
96
  };
@@ -44,7 +44,6 @@ const AI_PROFILE = {
44
44
  "Agent",
45
45
  "Bash",
46
46
  "Edit",
47
- "MultiEdit",
48
47
  "Write"
49
48
  ],
50
49
  permissionMode: "dontAsk",
@@ -61,7 +60,6 @@ const AI_PROFILE = {
61
60
  disallow: [
62
61
  "Agent",
63
62
  "Bash",
64
- "MultiEdit",
65
63
  "WebFetch",
66
64
  "WebSearch",
67
65
  "Write"
@@ -87,7 +85,6 @@ const AI_PROFILE = {
87
85
  "Edit",
88
86
  "Glob",
89
87
  "Grep",
90
- "MultiEdit",
91
88
  "Read",
92
89
  "Write"
93
90
  ]
@@ -105,7 +102,6 @@ const AI_PROFILE = {
105
102
  "Edit",
106
103
  "Glob",
107
104
  "Grep",
108
- "MultiEdit",
109
105
  "Read",
110
106
  "Write"
111
107
  ]
@@ -127,7 +123,6 @@ const AI_PROFILE = {
127
123
  "Edit",
128
124
  "Glob",
129
125
  "Grep",
130
- "MultiEdit",
131
126
  "Read",
132
127
  "Write"
133
128
  ]
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @file Declarative ROLE → (tier, profile) orchestration for AI call-outs. A
3
+ * caller names the ROLE its spawn plays — discovery, execution, planning,
4
+ * review, verification — and gets the fleet-policy (model, effort) pair via
5
+ * `AI_TIER` plus the matching permission `AI_PROFILE`, instead of hardcoding
6
+ * a model string and hand-pairing a profile at every site.
7
+ * The policy encodes fleet doctrine (see docs/agents.md/fleet/{token-spend,
8
+ * delegating-execution,fable-fallback}.md): Fable is the planning/review
9
+ * brain; execution + verification run on the sonnet floor to save tokens
10
+ * (mechanical execution drops to haiku); Opus is reserved for
11
+ * security-sensitive planning/review — never Fable there.
12
+ * Everything downstream is reuse: `spawnForRole` delegates to
13
+ * `spawnTierWithFallback`, so the model/effort table (`AI_TIER`), the
14
+ * availability-gated cross-engine fallback (`route.mts`), and the
15
+ * Fable-adaptive-only effort omission (`spawn.mts`) stay the single source.
16
+ * Model + effort can never be mismatched or omitted here — the tier supplies
17
+ * both, and the profile supplies tools/permission, so a caller cannot drift
18
+ * them apart.
19
+ */
20
+ import { AI_PROFILE } from './profiles.mts';
21
+ import type { RouteContext } from './route.mts';
22
+ import type { TierSpawnResult } from './spawn.mts';
23
+ import type { AiTier } from './tier.mts';
24
+ import type { SpawnAiAgentOptions } from './types.mts';
25
+ export type AiRole = 'discovery' | 'execution' | 'planning' | 'review' | 'verification';
26
+ export type RoleSensitivity = 'benign' | 'security';
27
+ export interface RoleTierLevers {
28
+ readonly mechanical?: boolean | undefined;
29
+ readonly sensitivity?: RoleSensitivity | undefined;
30
+ }
31
+ export interface RolePolicy {
32
+ readonly tier: AiTier;
33
+ readonly securityTier?: AiTier | undefined;
34
+ readonly profile: keyof typeof AI_PROFILE;
35
+ }
36
+ /**
37
+ * The role → policy table. The one place a role's tier + permission profile
38
+ * live; consumers derive, never re-encode.
39
+ */
40
+ export declare const ROLE_POLICY: Readonly<Record<AiRole, RolePolicy>>;
41
+ /**
42
+ * Resolve the tier a role runs at, applying the two levers: `security`
43
+ * sensitivity escalates to the role's `securityTier` (planning/review → opus),
44
+ * and `mechanical` execution drops to the haiku floor. Pure — no I/O.
45
+ */
46
+ export declare function resolveRoleTier(role: AiRole, options?: RoleTierLevers | undefined): AiTier;
47
+ export interface SpawnForRoleOptions extends Omit<SpawnAiAgentOptions, 'agent' | 'allow' | 'disallow' | 'effort' | 'model' | 'permissionMode' | 'tools'> {
48
+ readonly sensitivity?: RoleSensitivity | undefined;
49
+ readonly mechanical?: boolean | undefined;
50
+ }
51
+ /**
52
+ * Spawn an AI call-out by the ROLE it plays. Resolves role → tier (via
53
+ * `resolveRoleTier`) and role → permission profile (via `AI_PROFILE`), then
54
+ * delegates to `spawnTierWithFallback` so model/effort/fallback/Fable handling
55
+ * stay in the canonical tier layer. The caller supplies only prompt/cwd/etc —
56
+ * never a model, effort, or profile.
57
+ */
58
+ export declare function spawnForRole(role: AiRole, ctx: RouteContext, options: SpawnForRoleOptions): Promise<TierSpawnResult>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with rolldown */
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_ai_spawn = require('./spawn.js');
5
+ const require_ai_profiles = require('./profiles.js');
6
+
7
+ //#region src/ai/role.mts
8
+ /**
9
+ * @file Declarative ROLE → (tier, profile) orchestration for AI call-outs. A
10
+ * caller names the ROLE its spawn plays — discovery, execution, planning,
11
+ * review, verification — and gets the fleet-policy (model, effort) pair via
12
+ * `AI_TIER` plus the matching permission `AI_PROFILE`, instead of hardcoding
13
+ * a model string and hand-pairing a profile at every site.
14
+ * The policy encodes fleet doctrine (see docs/agents.md/fleet/{token-spend,
15
+ * delegating-execution,fable-fallback}.md): Fable is the planning/review
16
+ * brain; execution + verification run on the sonnet floor to save tokens
17
+ * (mechanical execution drops to haiku); Opus is reserved for
18
+ * security-sensitive planning/review — never Fable there.
19
+ * Everything downstream is reuse: `spawnForRole` delegates to
20
+ * `spawnTierWithFallback`, so the model/effort table (`AI_TIER`), the
21
+ * availability-gated cross-engine fallback (`route.mts`), and the
22
+ * Fable-adaptive-only effort omission (`spawn.mts`) stay the single source.
23
+ * Model + effort can never be mismatched or omitted here — the tier supplies
24
+ * both, and the profile supplies tools/permission, so a caller cannot drift
25
+ * them apart.
26
+ */
27
+ /**
28
+ * The role → policy table. The one place a role's tier + permission profile
29
+ * live; consumers derive, never re-encode.
30
+ */
31
+ const ROLE_POLICY = {
32
+ discovery: {
33
+ profile: "read",
34
+ tier: "sonnet"
35
+ },
36
+ execution: {
37
+ profile: "verify",
38
+ tier: "sonnet"
39
+ },
40
+ planning: {
41
+ profile: "read",
42
+ securityTier: "opus",
43
+ tier: "fable"
44
+ },
45
+ review: {
46
+ profile: "read",
47
+ securityTier: "opus",
48
+ tier: "fable"
49
+ },
50
+ verification: {
51
+ profile: "verify",
52
+ tier: "sonnet"
53
+ }
54
+ };
55
+ /**
56
+ * Resolve the tier a role runs at, applying the two levers: `security`
57
+ * sensitivity escalates to the role's `securityTier` (planning/review → opus),
58
+ * and `mechanical` execution drops to the haiku floor. Pure — no I/O.
59
+ */
60
+ function resolveRoleTier(role, options) {
61
+ const opts = {
62
+ __proto__: null,
63
+ ...options
64
+ };
65
+ const policy = ROLE_POLICY[role];
66
+ if (opts.mechanical && role === "execution") return "haiku";
67
+ if (opts.sensitivity === "security" && policy.securityTier !== void 0) return policy.securityTier;
68
+ return policy.tier;
69
+ }
70
+ /**
71
+ * Spawn an AI call-out by the ROLE it plays. Resolves role → tier (via
72
+ * `resolveRoleTier`) and role → permission profile (via `AI_PROFILE`), then
73
+ * delegates to `spawnTierWithFallback` so model/effort/fallback/Fable handling
74
+ * stay in the canonical tier layer. The caller supplies only prompt/cwd/etc —
75
+ * never a model, effort, or profile.
76
+ */
77
+ function spawnForRole(role, ctx, options) {
78
+ const { mechanical, sensitivity, ...rest } = {
79
+ __proto__: null,
80
+ ...options
81
+ };
82
+ const tier = resolveRoleTier(role, {
83
+ mechanical,
84
+ sensitivity
85
+ });
86
+ const profile = require_ai_profiles.AI_PROFILE[ROLE_POLICY[role].profile];
87
+ return require_ai_spawn.spawnTierWithFallback(tier, ctx, {
88
+ ...profile,
89
+ ...rest
90
+ });
91
+ }
92
+
93
+ //#endregion
94
+ exports.ROLE_POLICY = ROLE_POLICY;
95
+ exports.resolveRoleTier = resolveRoleTier;
96
+ exports.spawnForRole = spawnForRole;
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @file Billing-aware routing heuristic — a PURE policy layer over `route.mts`.
3
+ * `route.mts` answers "which cross-engine candidates for this tier are
4
+ * installed + keyed, in static preference order"; this module answers "given
5
+ * how each provider is BILLED (a subscription seat vs a flat-rate plan vs
6
+ * metered dollars), how much budget headroom remains, and whether we're in
7
+ * local dev or a locked-down CI — REORDER those candidates so the cheapest
8
+ * ration that still meets the tier's quality bar runs first." It is pure (no
9
+ * I/O): a caller fans out the billing / headroom probes once (the discovery
10
+ * layer) and passes a `BillingContext` in, exactly as `route.mts` takes a
11
+ * probed `RouteContext`. The candidates within a tier chain are cross-engine
12
+ * EQUIVALENTS at that capability level, so reordering them by cost is safe —
13
+ * the tier already fixed the quality bar. NO per-user constants live here.
14
+ * Every dollar limit, plan tier, member count, and promo window is DATA on
15
+ * the passed-in context; this file is formula + ordering only, so the same
16
+ * policy serves a metered org with a big budget, a free-promo subscription
17
+ * seat, a shared team pool, or a member with no credentials at all. Even
18
+ * "which model is suspended" is not encoded here — that is the
19
+ * route/availability layer's concern. Two design choices keep it a "sweet
20
+ * spot" rather than a blunt cost-sort: high-value tiers (opus/fable) stay
21
+ * QUALITY-FIRST (the Claude head leads) and only DEMOTE an account under
22
+ * budget pressure, while commodity tiers (haiku/sonnet) in local dev PROMOTE
23
+ * the cheapest equivalent to preserve the metered budget for work that needs
24
+ * it; and CI keeps the locked static order so a headless run never reorders
25
+ * away from its single available provider.
26
+ */
27
+ import type { CredentialProvider } from './credentials.mts';
28
+ import type { RouteContext, TierCandidate } from './route.mts';
29
+ import type { AiTier } from './tier.mts';
30
+ /**
31
+ * How a provider's credential is billed, which decides what the marginal token
32
+ * actually costs:
33
+ *
34
+ * - `flat-rate` — a fixed monthly plan with a usage window (e.g. N requests per
35
+ * 5h). Marginal cost is $0 while the window has room.
36
+ * - `metered` — pay per token against a (usually large, readable) monthly spend
37
+ * limit. Marginal cost is real dollars.
38
+ * - `subscription` — a seat (Claude Max, ChatGPT Pro) with an opaque weekly
39
+ * quota. Marginal cost is $0 until the quota is hit, then it stops.
40
+ */
41
+ export type BillingKind = 'flat-rate' | 'metered' | 'subscription';
42
+ /**
43
+ * Where routing is running. `local` dev has the full discovered provider set
44
+ * and may chase the cheapest equivalent; `ci` is locked to the secrets actually
45
+ * present (often a single provider) under the four-flag lockdown, so the policy
46
+ * keeps the static order and never reorders away from the one available lane.
47
+ */
48
+ export type RoutingEnv = 'ci' | 'local';
49
+ /**
50
+ * The shape of a unit of work, mapped to a capability tier by
51
+ * `taskClassToTier`. Orthogonal to billing: the class fixes the quality bar,
52
+ * billing decides which equivalent at that bar is cheapest.
53
+ */
54
+ export type TaskClass = 'agentic' | 'code' | 'grunt' | 'plan';
55
+ /**
56
+ * Remaining-headroom signal for one account, as read by the discovery layer
57
+ * from the provider's live API (metered: spend ÷ limit; flat-rate: window used
58
+ * ÷ window limit). `fraction` is remaining headroom in [0, 1] (1 = full, 0 =
59
+ * none), or `undefined` when no signal is available — a subscription seat with
60
+ * no programmatic quota read, which the policy then treats reactively.
61
+ * `exhausted` is the hard stop: a cap was hit or a 429 was observed, so the
62
+ * account should fall to the back of the order regardless of `fraction`.
63
+ */
64
+ export interface BillingHeadroom {
65
+ readonly exhausted: boolean;
66
+ readonly fraction: number | undefined;
67
+ }
68
+ /**
69
+ * One provider's billing situation for the credential that will actually be
70
+ * used (the resolver yields one token per provider, so one account per provider
71
+ * is the routing-relevant view). Multi-account-per-provider accounting belongs
72
+ * to the budget layer, not the candidate order.
73
+ */
74
+ export interface BillingAccount {
75
+ readonly headroom?: BillingHeadroom | undefined;
76
+ readonly kind: BillingKind;
77
+ readonly prefer?: number | undefined;
78
+ readonly provider: CredentialProvider;
79
+ }
80
+ /**
81
+ * The per-provider billing view a caller probes once and passes in — the
82
+ * billing analog of `RouteContext`. Keyed by provider; a provider absent from
83
+ * the map has no known billing signal and is left in static order.
84
+ */
85
+ export type BillingContext = Readonly<Partial<Record<CredentialProvider, BillingAccount>>>;
86
+ /**
87
+ * Canonical task-class → tier. The class fixes the capability bar; the tier
88
+ * chain then offers cross-engine equivalents at that bar for billing to
89
+ * reorder.
90
+ *
91
+ * - `grunt` → `haiku` — classify / summarize / single-token rewrites.
92
+ * - `code` → `sonnet` — control-flow / caller-chain edits, feature work.
93
+ * - `agentic` → `opus` — long tool-use loops, high-value autonomous work.
94
+ * - `plan` → `opus` — decomposition / architecture (escalate to `fable` only
95
+ * explicitly, never by default — and only when it is not suspended).
96
+ */
97
+ export declare const TASK_CLASS_TIER: Readonly<Record<TaskClass, AiTier>>;
98
+ /**
99
+ * Marginal-cost rank by billing kind, cheapest ration first (lower =
100
+ * preferred). Used only for commodity tiers in local dev — high-value tiers
101
+ * stay quality-first. Flat-rate and subscription are both $0-until-cap;
102
+ * flat-rate leads because its window resets fast (hours) vs a subscription's
103
+ * weekly cap.
104
+ */
105
+ export declare const COST_RANK: Readonly<Record<BillingKind, number>>;
106
+ /**
107
+ * Default per-task-class budget weights for `allocateBudget`. Reserves more of
108
+ * a member's share for high-value work (plan/agentic) than for bulk
109
+ * (grunt/code). Overridable per call — these are sane defaults, not per-user
110
+ * values.
111
+ */
112
+ export declare const DEFAULT_TASK_CLASS_WEIGHTS: Readonly<Record<TaskClass, number>>;
113
+ /**
114
+ * Headroom fraction at or below which a metered/flat-rate account is demoted
115
+ * (predictive backoff before the hard cap). A config knob with a sane default;
116
+ * callers override per `orderCandidates` call.
117
+ */
118
+ export declare const DEFAULT_DEMOTE_THRESHOLD = 0.2;
119
+ /**
120
+ * Per-task-class dollar caps. A generic allocation, not a per-user table —
121
+ * computed from a total + member count + weights, all of which are inputs.
122
+ */
123
+ export type BudgetAllocation = Readonly<Record<TaskClass, number>>;
124
+ export interface BudgetAllocationOptions {
125
+ readonly totalUsd: number;
126
+ readonly members?: number | undefined;
127
+ readonly weights?: Readonly<Partial<Record<TaskClass, number>>> | undefined;
128
+ }
129
+ /**
130
+ * Turn a total budget into reasonable per-member, per-task-class caps by a
131
+ * generic formula: split the total evenly across members, then distribute each
132
+ * member's share across task classes proportional to their weights. The numbers
133
+ * in are private; the formula is generic and committed. A non-positive total or
134
+ * member count degrades to zero caps rather than throwing.
135
+ */
136
+ export declare function allocateBudget(options: BudgetAllocationOptions): BudgetAllocation;
137
+ export interface CandidateScoreOptions {
138
+ readonly account: BillingAccount | undefined;
139
+ readonly demoteThreshold: number;
140
+ readonly promoteCheap: boolean;
141
+ }
142
+ /**
143
+ * Score one candidate for ordering (lower sorts earlier). A candidate with no
144
+ * known billing account scores neutral so it keeps its static position. The
145
+ * bands: a hard-exhausted account sinks to the back; a near-cap account is
146
+ * demoted; cost rank nudges commodity tiers toward the cheapest ration; an
147
+ * operator `prefer` nudge pulls a provider earlier. Pure.
148
+ */
149
+ export declare function candidateScore(options: CandidateScoreOptions): number;
150
+ export interface OrderCandidatesOptions {
151
+ readonly billing: BillingContext;
152
+ readonly demoteThreshold?: number | undefined;
153
+ readonly env?: RoutingEnv | undefined;
154
+ readonly route: RouteContext;
155
+ readonly tier: AiTier;
156
+ }
157
+ /**
158
+ * Reorder a tier's usable candidates by the billing policy, returning the
159
+ * runtime-fallback sequence (most-preferred first). Wraps
160
+ * `usableTierCandidates` — it never mutates the static `TIER_CHAINS`. The sort
161
+ * is stable on the original preference index, so candidates that score equal
162
+ * keep their static order (the Claude head stays first among equals).
163
+ *
164
+ * - In `ci`, or on a high-value tier (opus/fable), cost rank does NOT apply: the
165
+ * static quality-first order holds and only budget demotion / exhaustion
166
+ * reorders it.
167
+ * - In `local` on a commodity tier (haiku/sonnet), the cheapest billing kind is
168
+ * promoted ahead of a pricier equivalent, preserving the metered budget for
169
+ * the work that needs it.
170
+ */
171
+ export declare function orderCandidates(options: OrderCandidatesOptions): TierCandidate[];
172
+ /**
173
+ * Resolve a task class to its capability tier, defaulting to `sonnet` for an
174
+ * unknown label so a stray string degrades safely rather than yielding
175
+ * `undefined`.
176
+ */
177
+ export declare function taskClassToTier(taskClass: TaskClass): AiTier;
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+ /* Socket Lib - Built with rolldown */
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_ai_route = require('./route.js');
5
+
6
+ //#region src/ai/route-heuristic.mts
7
+ /**
8
+ * @file Billing-aware routing heuristic — a PURE policy layer over `route.mts`.
9
+ * `route.mts` answers "which cross-engine candidates for this tier are
10
+ * installed + keyed, in static preference order"; this module answers "given
11
+ * how each provider is BILLED (a subscription seat vs a flat-rate plan vs
12
+ * metered dollars), how much budget headroom remains, and whether we're in
13
+ * local dev or a locked-down CI — REORDER those candidates so the cheapest
14
+ * ration that still meets the tier's quality bar runs first." It is pure (no
15
+ * I/O): a caller fans out the billing / headroom probes once (the discovery
16
+ * layer) and passes a `BillingContext` in, exactly as `route.mts` takes a
17
+ * probed `RouteContext`. The candidates within a tier chain are cross-engine
18
+ * EQUIVALENTS at that capability level, so reordering them by cost is safe —
19
+ * the tier already fixed the quality bar. NO per-user constants live here.
20
+ * Every dollar limit, plan tier, member count, and promo window is DATA on
21
+ * the passed-in context; this file is formula + ordering only, so the same
22
+ * policy serves a metered org with a big budget, a free-promo subscription
23
+ * seat, a shared team pool, or a member with no credentials at all. Even
24
+ * "which model is suspended" is not encoded here — that is the
25
+ * route/availability layer's concern. Two design choices keep it a "sweet
26
+ * spot" rather than a blunt cost-sort: high-value tiers (opus/fable) stay
27
+ * QUALITY-FIRST (the Claude head leads) and only DEMOTE an account under
28
+ * budget pressure, while commodity tiers (haiku/sonnet) in local dev PROMOTE
29
+ * the cheapest equivalent to preserve the metered budget for work that needs
30
+ * it; and CI keeps the locked static order so a headless run never reorders
31
+ * away from its single available provider.
32
+ */
33
+ /**
34
+ * Canonical task-class → tier. The class fixes the capability bar; the tier
35
+ * chain then offers cross-engine equivalents at that bar for billing to
36
+ * reorder.
37
+ *
38
+ * - `grunt` → `haiku` — classify / summarize / single-token rewrites.
39
+ * - `code` → `sonnet` — control-flow / caller-chain edits, feature work.
40
+ * - `agentic` → `opus` — long tool-use loops, high-value autonomous work.
41
+ * - `plan` → `opus` — decomposition / architecture (escalate to `fable` only
42
+ * explicitly, never by default — and only when it is not suspended).
43
+ */
44
+ const TASK_CLASS_TIER = {
45
+ __proto__: null,
46
+ agentic: "opus",
47
+ code: "sonnet",
48
+ grunt: "haiku",
49
+ plan: "opus"
50
+ };
51
+ /**
52
+ * Marginal-cost rank by billing kind, cheapest ration first (lower =
53
+ * preferred). Used only for commodity tiers in local dev — high-value tiers
54
+ * stay quality-first. Flat-rate and subscription are both $0-until-cap;
55
+ * flat-rate leads because its window resets fast (hours) vs a subscription's
56
+ * weekly cap.
57
+ */
58
+ const COST_RANK = {
59
+ __proto__: null,
60
+ "flat-rate": 0,
61
+ metered: 2,
62
+ subscription: 1
63
+ };
64
+ /**
65
+ * Default per-task-class budget weights for `allocateBudget`. Reserves more of
66
+ * a member's share for high-value work (plan/agentic) than for bulk
67
+ * (grunt/code). Overridable per call — these are sane defaults, not per-user
68
+ * values.
69
+ */
70
+ const DEFAULT_TASK_CLASS_WEIGHTS = {
71
+ __proto__: null,
72
+ agentic: 3,
73
+ code: 2,
74
+ grunt: 1,
75
+ plan: 4
76
+ };
77
+ /**
78
+ * Headroom fraction at or below which a metered/flat-rate account is demoted
79
+ * (predictive backoff before the hard cap). A config knob with a sane default;
80
+ * callers override per `orderCandidates` call.
81
+ */
82
+ const DEFAULT_DEMOTE_THRESHOLD = .2;
83
+ const BULK_TIERS = /* @__PURE__ */ new Set(["haiku", "sonnet"]);
84
+ const SCORE_NEAR_CAP = 100;
85
+ const SCORE_EXHAUSTED = 1e4;
86
+ const TASK_CLASSES = [
87
+ "agentic",
88
+ "code",
89
+ "grunt",
90
+ "plan"
91
+ ];
92
+ /**
93
+ * Turn a total budget into reasonable per-member, per-task-class caps by a
94
+ * generic formula: split the total evenly across members, then distribute each
95
+ * member's share across task classes proportional to their weights. The numbers
96
+ * in are private; the formula is generic and committed. A non-positive total or
97
+ * member count degrades to zero caps rather than throwing.
98
+ */
99
+ function allocateBudget(options) {
100
+ const opts = {
101
+ __proto__: null,
102
+ ...options
103
+ };
104
+ const members = opts.members && opts.members > 0 ? opts.members : 1;
105
+ const perMember = (opts.totalUsd > 0 ? opts.totalUsd : 0) / members;
106
+ const weights = {
107
+ __proto__: null,
108
+ ...DEFAULT_TASK_CLASS_WEIGHTS,
109
+ ...opts.weights
110
+ };
111
+ let totalWeight = 0;
112
+ for (let i = 0, { length } = TASK_CLASSES; i < length; i += 1) totalWeight += weights[TASK_CLASSES[i]] ?? 0;
113
+ const divisor = totalWeight > 0 ? totalWeight : 1;
114
+ const out = { __proto__: null };
115
+ for (let i = 0, { length } = TASK_CLASSES; i < length; i += 1) {
116
+ const taskClass = TASK_CLASSES[i];
117
+ out[taskClass] = perMember * (weights[taskClass] ?? 0) / divisor;
118
+ }
119
+ return out;
120
+ }
121
+ /**
122
+ * Score one candidate for ordering (lower sorts earlier). A candidate with no
123
+ * known billing account scores neutral so it keeps its static position. The
124
+ * bands: a hard-exhausted account sinks to the back; a near-cap account is
125
+ * demoted; cost rank nudges commodity tiers toward the cheapest ration; an
126
+ * operator `prefer` nudge pulls a provider earlier. Pure.
127
+ */
128
+ function candidateScore(options) {
129
+ const opts = {
130
+ __proto__: null,
131
+ ...options
132
+ };
133
+ const { account } = opts;
134
+ if (!account) return 0;
135
+ let score = 0;
136
+ const { headroom } = account;
137
+ if (headroom?.exhausted) score += SCORE_EXHAUSTED;
138
+ else if (typeof headroom?.fraction === "number" && headroom.fraction <= opts.demoteThreshold) score += SCORE_NEAR_CAP;
139
+ if (opts.promoteCheap) score += COST_RANK[account.kind] ?? 0;
140
+ if (typeof account.prefer === "number") score -= account.prefer;
141
+ return score;
142
+ }
143
+ /**
144
+ * Reorder a tier's usable candidates by the billing policy, returning the
145
+ * runtime-fallback sequence (most-preferred first). Wraps
146
+ * `usableTierCandidates` — it never mutates the static `TIER_CHAINS`. The sort
147
+ * is stable on the original preference index, so candidates that score equal
148
+ * keep their static order (the Claude head stays first among equals).
149
+ *
150
+ * - In `ci`, or on a high-value tier (opus/fable), cost rank does NOT apply: the
151
+ * static quality-first order holds and only budget demotion / exhaustion
152
+ * reorders it.
153
+ * - In `local` on a commodity tier (haiku/sonnet), the cheapest billing kind is
154
+ * promoted ahead of a pricier equivalent, preserving the metered budget for
155
+ * the work that needs it.
156
+ */
157
+ function orderCandidates(options) {
158
+ const opts = {
159
+ __proto__: null,
160
+ ...options
161
+ };
162
+ const env = opts.env ?? "local";
163
+ const demoteThreshold = opts.demoteThreshold ?? .2;
164
+ const promoteCheap = env === "local" && BULK_TIERS.has(opts.tier);
165
+ const scored = require_ai_route.usableTierCandidates(opts.tier, opts.route).map((candidate, index) => ({
166
+ candidate,
167
+ index,
168
+ score: candidateScore({
169
+ account: opts.billing[candidate.provider],
170
+ demoteThreshold,
171
+ promoteCheap
172
+ })
173
+ }));
174
+ scored.sort((a, b) => a.score - b.score || a.index - b.index);
175
+ return scored.map((entry) => entry.candidate);
176
+ }
177
+ /**
178
+ * Resolve a task class to its capability tier, defaulting to `sonnet` for an
179
+ * unknown label so a stray string degrades safely rather than yielding
180
+ * `undefined`.
181
+ */
182
+ function taskClassToTier(taskClass) {
183
+ return TASK_CLASS_TIER[taskClass] ?? "sonnet";
184
+ }
185
+
186
+ //#endregion
187
+ exports.COST_RANK = COST_RANK;
188
+ exports.DEFAULT_DEMOTE_THRESHOLD = DEFAULT_DEMOTE_THRESHOLD;
189
+ exports.DEFAULT_TASK_CLASS_WEIGHTS = DEFAULT_TASK_CLASS_WEIGHTS;
190
+ exports.TASK_CLASS_TIER = TASK_CLASS_TIER;
191
+ exports.allocateBudget = allocateBudget;
192
+ exports.candidateScore = candidateScore;
193
+ exports.orderCandidates = orderCandidates;
194
+ exports.taskClassToTier = taskClassToTier;
@@ -6,15 +6,16 @@
6
6
  * credential for it is resolvable; otherwise the resolver walks a
7
7
  * cross-engine equivalence ladder (Claude → Codex → an open-weight provider
8
8
  * via opencode) and returns the best available equivalent. Why gate on
9
- * existence: a fleet machine may have Claude but no Codex, or Codex but an
10
- * expired Claude key, or neither plus an opencode/synthetic seat. Hard-coding
11
- * `fable` then fails at spawn time; routing here degrades gracefully and
12
- * tells the caller WHY (the `reason`), so a skill can log "fell back to codex
13
- * gpt-5.5 (claude unavailable)" instead of crashing. Pure given an
14
- * availability/keyed context — no I/O — so callers fan out their `which` +
15
- * credential probes once and pass the result in. Pairs with `buildArgs` in
16
- * `spawn.mts`: a Fable candidate carries `effort: undefined` because Fable is
17
- * adaptive-thinking-only and the spawn layer omits `--effort` for it anyway.
9
+ * existence: a Socket dev machine may have Claude but no Codex, or Codex but
10
+ * an expired Claude key, or neither plus an opencode/synthetic seat.
11
+ * Hard-coding `fable` then fails at spawn time; routing here degrades
12
+ * gracefully and tells the caller WHY (the `reason`), so a skill can log
13
+ * "fell back to codex gpt-5.5 (claude unavailable)" instead of crashing. Pure
14
+ * given an availability/keyed context — no I/O — so callers fan out their
15
+ * `which` + credential probes once and pass the result in. Pairs with
16
+ * `buildArgs` in `spawn.mts`: a Fable candidate carries `effort: undefined`
17
+ * because Fable is adaptive-thinking-only and the spawn layer omits
18
+ * `--effort` for it anyway.
18
19
  */
19
20
  import type { CredentialProvider } from './credentials.mts';
20
21
  import type { AiAgentName, AiEffort } from './types.mts';