@jinn-network/client 0.1.7-canary.d4e4e183 → 0.1.7-canary.e68c8f43

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 (297) hide show
  1. package/README.md +67 -1
  2. package/dist/adapters/mech/adapter.d.ts +19 -1
  3. package/dist/adapters/mech/adapter.js +130 -14
  4. package/dist/adapters/mech/adapter.js.map +1 -1
  5. package/dist/adapters/mech/contracts.js +11 -2
  6. package/dist/adapters/mech/contracts.js.map +1 -1
  7. package/dist/adapters/mech/safe.d.ts +1 -1
  8. package/dist/adapters/mech/safe.js +5 -3
  9. package/dist/adapters/mech/safe.js.map +1 -1
  10. package/dist/adapters/mech/types.d.ts +6 -1
  11. package/dist/adapters/mech/types.js.map +1 -1
  12. package/dist/agent/operator-claude.js +8 -0
  13. package/dist/agent/operator-claude.js.map +1 -1
  14. package/dist/api/activity-events-endpoint.d.ts +14 -0
  15. package/dist/api/activity-events-endpoint.js +59 -0
  16. package/dist/api/activity-events-endpoint.js.map +1 -0
  17. package/dist/api/bootstrap-endpoint.d.ts +1 -2
  18. package/dist/api/bootstrap-endpoint.js +42 -24
  19. package/dist/api/bootstrap-endpoint.js.map +1 -1
  20. package/dist/api/codex-doctor-endpoint.d.ts +22 -5
  21. package/dist/api/codex-doctor-endpoint.js +136 -17
  22. package/dist/api/codex-doctor-endpoint.js.map +1 -1
  23. package/dist/api/debug-report-endpoint.d.ts +27 -0
  24. package/dist/api/debug-report-endpoint.js +157 -0
  25. package/dist/api/debug-report-endpoint.js.map +1 -0
  26. package/dist/api/gather-status.d.ts +33 -0
  27. package/dist/api/gather-status.js +210 -26
  28. package/dist/api/gather-status.js.map +1 -1
  29. package/dist/api/hermes-doctor-endpoint.d.ts +15 -7
  30. package/dist/api/hermes-doctor-endpoint.js +56 -19
  31. package/dist/api/hermes-doctor-endpoint.js.map +1 -1
  32. package/dist/api/launcher-status.d.ts +4 -2
  33. package/dist/api/launcher-status.js +11 -10
  34. package/dist/api/launcher-status.js.map +1 -1
  35. package/dist/api/launcher-tasks.d.ts +1 -1
  36. package/dist/api/launcher-tasks.js +12 -8
  37. package/dist/api/launcher-tasks.js.map +1 -1
  38. package/dist/api/operator-artifacts-endpoint.js +73 -6
  39. package/dist/api/operator-artifacts-endpoint.js.map +1 -1
  40. package/dist/api/server.d.ts +17 -0
  41. package/dist/api/server.js +40 -1
  42. package/dist/api/server.js.map +1 -1
  43. package/dist/api/setup-endpoints.d.ts +0 -9
  44. package/dist/api/setup-endpoints.js +11 -153
  45. package/dist/api/setup-endpoints.js.map +1 -1
  46. package/dist/api/solvernets-endpoints.js +30 -63
  47. package/dist/api/solvernets-endpoints.js.map +1 -1
  48. package/dist/api/status-build.d.ts +115 -2
  49. package/dist/api/status-build.js +47 -11
  50. package/dist/api/status-build.js.map +1 -1
  51. package/dist/api/status-harness-rollup.d.ts +35 -0
  52. package/dist/api/status-harness-rollup.js +45 -0
  53. package/dist/api/status-harness-rollup.js.map +1 -0
  54. package/dist/build-info.json +4 -4
  55. package/dist/build-meta.json +1 -1
  56. package/dist/captures/live-publisher.js +24 -4
  57. package/dist/captures/live-publisher.js.map +1 -1
  58. package/dist/captures/publish.d.ts +1 -1
  59. package/dist/chain-read-errors.d.ts +12 -0
  60. package/dist/chain-read-errors.js +26 -1
  61. package/dist/chain-read-errors.js.map +1 -1
  62. package/dist/cli/commands/codedigest-revert-check.d.ts +33 -0
  63. package/dist/cli/commands/codedigest-revert-check.js +249 -0
  64. package/dist/cli/commands/codedigest-revert-check.js.map +1 -0
  65. package/dist/cli/commands/solver-nets.d.ts +1 -0
  66. package/dist/cli/commands/solver-nets.js +170 -22
  67. package/dist/cli/commands/solver-nets.js.map +1 -1
  68. package/dist/cli/commands/solver-plugins-block.d.ts +33 -0
  69. package/dist/cli/commands/solver-plugins-block.js +118 -0
  70. package/dist/cli/commands/solver-plugins-block.js.map +1 -0
  71. package/dist/cli/commands/solver-plugins-feedback.d.ts +72 -0
  72. package/dist/cli/commands/solver-plugins-feedback.js +262 -0
  73. package/dist/cli/commands/solver-plugins-feedback.js.map +1 -0
  74. package/dist/cli/commands/solver-plugins-read.d.ts +54 -0
  75. package/dist/cli/commands/solver-plugins-read.js +259 -0
  76. package/dist/cli/commands/solver-plugins-read.js.map +1 -0
  77. package/dist/cli/commands/solver-plugins.d.ts +35 -0
  78. package/dist/cli/commands/solver-plugins.js +399 -2
  79. package/dist/cli/commands/solver-plugins.js.map +1 -1
  80. package/dist/cli/commands/tasks.js +15 -2
  81. package/dist/cli/commands/tasks.js.map +1 -1
  82. package/dist/cli/index.js +2 -0
  83. package/dist/cli/index.js.map +1 -1
  84. package/dist/cli/task-native-readiness.d.ts +7 -0
  85. package/dist/cli/task-native-readiness.js +7 -5
  86. package/dist/cli/task-native-readiness.js.map +1 -1
  87. package/dist/config.d.ts +183 -232
  88. package/dist/config.js +232 -107
  89. package/dist/config.js.map +1 -1
  90. package/dist/daemon/ai-units-gate.d.ts +54 -0
  91. package/dist/daemon/ai-units-gate.js +82 -0
  92. package/dist/daemon/ai-units-gate.js.map +1 -0
  93. package/dist/daemon/creator.js +13 -0
  94. package/dist/daemon/creator.js.map +1 -1
  95. package/dist/daemon/daemon.d.ts +10 -0
  96. package/dist/daemon/daemon.js +203 -30
  97. package/dist/daemon/daemon.js.map +1 -1
  98. package/dist/daemon/gate-logger.d.ts +9 -0
  99. package/dist/daemon/gate-logger.js +2 -0
  100. package/dist/daemon/gate-logger.js.map +1 -0
  101. package/dist/daemon/readiness-gate.d.ts +1 -4
  102. package/dist/daemon/readiness-gate.js.map +1 -1
  103. package/dist/daemon/spend-cap-gate.d.ts +40 -0
  104. package/dist/daemon/spend-cap-gate.js +46 -0
  105. package/dist/daemon/spend-cap-gate.js.map +1 -0
  106. package/dist/dashboard/assets/index-CvQbxjxy.js +351 -0
  107. package/dist/dashboard/assets/index-CzKxvMcU.css +32 -0
  108. package/dist/dashboard/index.html +2 -2
  109. package/dist/discovery/http.js +328 -1
  110. package/dist/discovery/http.js.map +1 -1
  111. package/dist/discovery/onchain.js +42 -4
  112. package/dist/discovery/onchain.js.map +1 -1
  113. package/dist/discovery/types.d.ts +129 -0
  114. package/dist/discovery/types.js.map +1 -1
  115. package/dist/discovery/with-fallback.js +27 -0
  116. package/dist/discovery/with-fallback.js.map +1 -1
  117. package/dist/earning/bootstrap.d.ts +8 -3
  118. package/dist/earning/bootstrap.js +36 -13
  119. package/dist/earning/bootstrap.js.map +1 -1
  120. package/dist/earning/safe-adapter.js +23 -11
  121. package/dist/earning/safe-adapter.js.map +1 -1
  122. package/dist/earning/types.d.ts +6 -6
  123. package/dist/earning/viem-clients.d.ts +11 -4
  124. package/dist/earning/viem-clients.js +14 -5
  125. package/dist/earning/viem-clients.js.map +1 -1
  126. package/dist/erc8004/identity.d.ts +19 -3
  127. package/dist/erc8004/identity.js +21 -6
  128. package/dist/erc8004/identity.js.map +1 -1
  129. package/dist/erc8004/index.d.ts +1 -1
  130. package/dist/erc8004/index.js.map +1 -1
  131. package/dist/events/types.d.ts +2 -2
  132. package/dist/harnesses/cost-estimates.d.ts +10 -31
  133. package/dist/harnesses/cost-estimates.js +11 -43
  134. package/dist/harnesses/cost-estimates.js.map +1 -1
  135. package/dist/harnesses/engine/engine.d.ts +9 -3
  136. package/dist/harnesses/engine/engine.js +51 -6
  137. package/dist/harnesses/engine/engine.js.map +1 -1
  138. package/dist/harnesses/impls/hermes-agent/bootstrap.js +4 -2
  139. package/dist/harnesses/impls/hermes-agent/bootstrap.js.map +1 -1
  140. package/dist/harnesses/impls/hermes-agent/config-builder.d.ts +1 -1
  141. package/dist/harnesses/impls/hermes-agent/config-builder.js +4 -2
  142. package/dist/harnesses/impls/hermes-agent/config-builder.js.map +1 -1
  143. package/dist/harnesses/impls/hermes-agent/harness.js +2 -2
  144. package/dist/harnesses/impls/hermes-agent/harness.js.map +1 -1
  145. package/dist/harnesses/impls/hermes-agent/prompt.d.ts +6 -6
  146. package/dist/harnesses/impls/hermes-agent/prompt.js +6 -6
  147. package/dist/harnesses/impls/learner/adapters/claude-code.d.ts +17 -0
  148. package/dist/harnesses/impls/learner/adapters/claude-code.js +113 -14
  149. package/dist/harnesses/impls/learner/adapters/claude-code.js.map +1 -1
  150. package/dist/harnesses/impls/learner/adapters/codex-code.d.ts +9 -0
  151. package/dist/harnesses/impls/learner/adapters/codex-code.js +30 -8
  152. package/dist/harnesses/impls/learner/adapters/codex-code.js.map +1 -1
  153. package/dist/harnesses/impls/learner/harness.d.ts +9 -0
  154. package/dist/harnesses/impls/learner/harness.js +12 -3
  155. package/dist/harnesses/impls/learner/harness.js.map +1 -1
  156. package/dist/harnesses/impls/learner/harvest.d.ts +1 -1
  157. package/dist/harnesses/impls/learner/harvest.js +23 -5
  158. package/dist/harnesses/impls/learner/harvest.js.map +1 -1
  159. package/dist/harnesses/impls/learner/restoration-patch.d.ts +2 -2
  160. package/dist/harnesses/impls/learner/restoration-patch.js +25 -6
  161. package/dist/harnesses/impls/learner/restoration-patch.js.map +1 -1
  162. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js +21 -1
  163. package/dist/harnesses/impls/swe-rebench-v2-evaluator/eval-runner.js.map +1 -1
  164. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.d.ts +74 -5
  165. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js +103 -32
  166. package/dist/harnesses/impls/swe-rebench-v2-evaluator/hf-fetcher.js.map +1 -1
  167. package/dist/harnesses/readiness-registry.d.ts +7 -0
  168. package/dist/harnesses/readiness-registry.js +9 -0
  169. package/dist/harnesses/readiness-registry.js.map +1 -1
  170. package/dist/learner/revert-decision.d.ts +59 -0
  171. package/dist/learner/revert-decision.js +53 -0
  172. package/dist/learner/revert-decision.js.map +1 -0
  173. package/dist/learner/revert-stats.d.ts +24 -0
  174. package/dist/learner/revert-stats.js +44 -0
  175. package/dist/learner/revert-stats.js.map +1 -0
  176. package/dist/main.js +177 -104
  177. package/dist/main.js.map +1 -1
  178. package/dist/mcp/get-codedigest-reward.d.ts +13 -0
  179. package/dist/mcp/get-codedigest-reward.js +23 -0
  180. package/dist/mcp/get-codedigest-reward.js.map +1 -0
  181. package/dist/mcp/server.js +23 -0
  182. package/dist/mcp/server.js.map +1 -1
  183. package/dist/observability/debug-report-assemble.d.ts +43 -0
  184. package/dist/observability/debug-report-assemble.js +80 -0
  185. package/dist/observability/debug-report-assemble.js.map +1 -0
  186. package/dist/observability/emit-event.d.ts +9 -2
  187. package/dist/observability/emit-event.js +36 -2
  188. package/dist/observability/emit-event.js.map +1 -1
  189. package/dist/observability/file-logger.d.ts +69 -0
  190. package/dist/observability/file-logger.js +177 -0
  191. package/dist/observability/file-logger.js.map +1 -0
  192. package/dist/observability/redact-secrets.d.ts +65 -0
  193. package/dist/observability/redact-secrets.js +300 -0
  194. package/dist/observability/redact-secrets.js.map +1 -0
  195. package/dist/observability/tar.d.ts +30 -0
  196. package/dist/observability/tar.js +102 -0
  197. package/dist/observability/tar.js.map +1 -0
  198. package/dist/plugins/learner/skills/learn/consolidator-prompt.md +18 -1
  199. package/dist/plugins/learner/skills/learn/promoter-prompt.md +72 -1
  200. package/dist/preflight/pidfile-liveness.d.ts +44 -0
  201. package/dist/preflight/pidfile-liveness.js +103 -0
  202. package/dist/preflight/pidfile-liveness.js.map +1 -0
  203. package/dist/preflight/rpc-network.d.ts +40 -0
  204. package/dist/preflight/rpc-network.js +67 -1
  205. package/dist/preflight/rpc-network.js.map +1 -1
  206. package/dist/rpc/transport.d.ts +109 -0
  207. package/dist/rpc/transport.js +220 -0
  208. package/dist/rpc/transport.js.map +1 -0
  209. package/dist/scripts/donation-consumption-acceptance.js +7 -28
  210. package/dist/scripts/donation-consumption-acceptance.js.map +1 -1
  211. package/dist/scripts/swe-rebench-v2-pytest-missing.json +16 -0
  212. package/dist/solver-nets/prediction-operator-ux.d.ts +1 -2
  213. package/dist/solver-nets/prediction-operator-ux.js +56 -53
  214. package/dist/solver-nets/prediction-operator-ux.js.map +1 -1
  215. package/dist/solver-nets/registry.d.ts +19 -1
  216. package/dist/solver-nets/registry.js +37 -24
  217. package/dist/solver-nets/registry.js.map +1 -1
  218. package/dist/solver-types/_swe-rebench-v2-pool.d.ts +9 -2
  219. package/dist/solver-types/_swe-rebench-v2-pool.js +15 -20
  220. package/dist/solver-types/_swe-rebench-v2-pool.js.map +1 -1
  221. package/dist/solver-types/_swe-rebench-v2-state.d.ts +15 -0
  222. package/dist/solver-types/_swe-rebench-v2-state.js +19 -0
  223. package/dist/solver-types/_swe-rebench-v2-state.js.map +1 -1
  224. package/dist/solver-types/_swe-rebench-v2-validated-pool.d.ts +80 -2
  225. package/dist/solver-types/_swe-rebench-v2-validated-pool.js +242 -21
  226. package/dist/solver-types/_swe-rebench-v2-validated-pool.js.map +1 -1
  227. package/dist/solver-types/swe-rebench-v2-auto.d.ts +20 -11
  228. package/dist/solver-types/swe-rebench-v2-auto.js +64 -19
  229. package/dist/solver-types/swe-rebench-v2-auto.js.map +1 -1
  230. package/dist/solver-types/swe-rebench-v2.d.ts +8 -2
  231. package/dist/solver-types/swe-rebench-v2.js +127 -11
  232. package/dist/solver-types/swe-rebench-v2.js.map +1 -1
  233. package/dist/solvernets/launched-record-dispatcher.d.ts +4 -0
  234. package/dist/solvernets/launched-record-dispatcher.js +10 -4
  235. package/dist/solvernets/launched-record-dispatcher.js.map +1 -1
  236. package/dist/solvernets/registry-client-erc8004.js +11 -0
  237. package/dist/solvernets/registry-client-erc8004.js.map +1 -1
  238. package/dist/solvernets/store.d.ts +2 -2
  239. package/dist/spend/ai-units-config.d.ts +37 -0
  240. package/dist/spend/ai-units-config.js +26 -0
  241. package/dist/spend/ai-units-config.js.map +1 -0
  242. package/dist/spend/ai-units.d.ts +86 -0
  243. package/dist/spend/ai-units.js +163 -0
  244. package/dist/spend/ai-units.js.map +1 -0
  245. package/dist/spend/cost-surface-status.d.ts +10 -0
  246. package/dist/spend/cost-surface-status.js +22 -0
  247. package/dist/spend/cost-surface-status.js.map +1 -0
  248. package/dist/spend/credential.d.ts +8 -0
  249. package/dist/spend/credential.js +30 -0
  250. package/dist/spend/credential.js.map +1 -0
  251. package/dist/spend/daemon-config.d.ts +13 -0
  252. package/dist/spend/daemon-config.js +24 -0
  253. package/dist/spend/daemon-config.js.map +1 -0
  254. package/dist/spend/pricing.d.ts +16 -0
  255. package/dist/spend/pricing.js +26 -0
  256. package/dist/spend/pricing.js.map +1 -0
  257. package/dist/spend/record.d.ts +13 -0
  258. package/dist/spend/record.js +36 -0
  259. package/dist/spend/record.js.map +1 -0
  260. package/dist/spend/usage.d.ts +27 -0
  261. package/dist/spend/usage.js +113 -0
  262. package/dist/spend/usage.js.map +1 -0
  263. package/dist/store/store.d.ts +101 -0
  264. package/dist/store/store.js +304 -4
  265. package/dist/store/store.js.map +1 -1
  266. package/dist/trajectory/transcript-parsers/codex-session.d.ts +12 -6
  267. package/dist/trajectory/transcript-parsers/codex-session.js +114 -13
  268. package/dist/trajectory/transcript-parsers/codex-session.js.map +1 -1
  269. package/dist/trajectory/transcript-parsers/types.d.ts +8 -8
  270. package/dist/trajectory/transcript-session-dirs.d.ts +18 -0
  271. package/dist/trajectory/transcript-session-dirs.js +85 -0
  272. package/dist/trajectory/transcript-session-dirs.js.map +1 -0
  273. package/dist/trajectory/transcript-watcher.d.ts +20 -1
  274. package/dist/trajectory/transcript-watcher.js +98 -32
  275. package/dist/trajectory/transcript-watcher.js.map +1 -1
  276. package/dist/tx-retry.d.ts +15 -0
  277. package/dist/tx-retry.js +37 -7
  278. package/dist/tx-retry.js.map +1 -1
  279. package/dist/types/payloads/portfolio-v0.d.ts +3 -3
  280. package/dist/types/payloads/prediction-apy-v0.d.ts +3 -3
  281. package/dist/types/payloads/prediction-v0.d.ts +12 -12
  282. package/package.json +10 -3
  283. package/plugins/learner/skills/learn/consolidator-prompt.md +18 -1
  284. package/plugins/learner/skills/learn/promoter-prompt.md +72 -1
  285. package/plugins/swe-rebench-v2-diffmin/README.md +10 -9
  286. package/plugins/swe-rebench-v2-diffmin/jinn.plugin.json +1 -1
  287. package/plugins/swe-rebench-v2-diffmin/skills/diffmin/SKILL.md +15 -10
  288. package/plugins/swe-rebench-v2-diffmin/skills/test-map/SKILL.md +10 -12
  289. package/plugins/swe-rebench-v2-runtime/.claude-plugin/plugin.json +1 -1
  290. package/plugins/swe-rebench-v2-runtime/.codex-plugin/plugin.json +3 -3
  291. package/plugins/swe-rebench-v2-runtime/README.md +6 -6
  292. package/plugins/swe-rebench-v2-runtime/jinn.plugin.json +2 -3
  293. package/plugins/swe-rebench-v2-runtime/skills/task/SKILL.md +81 -0
  294. package/dist/dashboard/assets/index-BUlE8F3Y.js +0 -330
  295. package/dist/dashboard/assets/index-blqc7eqq.css +0 -32
  296. package/plugins/swe-rebench-v2-runtime/skills/orient/SKILL.md +0 -29
  297. package/plugins/swe-rebench-v2-runtime/skills/plan/SKILL.md +0 -53
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Daemon-side AI-units gate config — issue #815.
3
+ *
4
+ * Resolves, per-manifest:
5
+ * - the credential that manifest's harness bills against
6
+ * - the projected AI-unit cost of one task on that harness/model
7
+ *
8
+ * Plus the active per-block / per-week caps from
9
+ * `resolveReferenceCeiling(env)`.
10
+ *
11
+ * Returns undefined only when no joined SolverNet resolves to a
12
+ * credential the gate could meaningfully meter — at that point the
13
+ * gate stays off entirely so the daemon does not spend a query per
14
+ * task on rows that will never sum.
15
+ */
16
+ import type { JinnConfig } from '../config.js';
17
+ import { type CredentialId } from './credential.js';
18
+ export interface AiUnitsDaemonConfig {
19
+ capPerBlock: number;
20
+ capPerWeek: number;
21
+ /** manifestCid -> credentialId (set only when the harness has one). */
22
+ manifestCredentials: Record<string, CredentialId>;
23
+ /**
24
+ * manifestCid -> projected AI units per task. `null` means the model
25
+ * is unknown to the cost table and the gate must fail-open with a
26
+ * one-time warn rather than silently halting claims.
27
+ */
28
+ manifestProjectedAiUnits: Record<string, number | null>;
29
+ /**
30
+ * manifestCid -> the model id from the joined SolverNet config. Threaded
31
+ * onto the per-request activity row at claim time so the row carries
32
+ * provenance of the projection. May be undefined when the operator
33
+ * joined without selecting a model.
34
+ */
35
+ manifestModels: Record<string, string | undefined>;
36
+ }
37
+ export declare function buildAiUnitsConfig(config: Pick<JinnConfig, 'joinedSolverNets'>, env: NodeJS.ProcessEnv): AiUnitsDaemonConfig | undefined;
@@ -0,0 +1,26 @@
1
+ import { resolveCredentialId } from './credential.js';
2
+ import { projectAiUnits, resolveReferenceCeiling } from './ai-units.js';
3
+ export function buildAiUnitsConfig(config, env) {
4
+ const { units_per_block, units_per_week } = resolveReferenceCeiling(env);
5
+ const manifestCredentials = {};
6
+ const manifestProjectedAiUnits = {};
7
+ const manifestModels = {};
8
+ for (const [manifestCid, entry] of Object.entries(config.joinedSolverNets ?? {})) {
9
+ const credentialId = resolveCredentialId(entry.harness, env);
10
+ if (!credentialId)
11
+ continue;
12
+ manifestCredentials[manifestCid] = credentialId;
13
+ manifestProjectedAiUnits[manifestCid] = projectAiUnits(entry.harness, entry.model, credentialId);
14
+ manifestModels[manifestCid] = entry.model;
15
+ }
16
+ if (Object.keys(manifestCredentials).length === 0)
17
+ return undefined;
18
+ return {
19
+ capPerBlock: units_per_block,
20
+ capPerWeek: units_per_week,
21
+ manifestCredentials,
22
+ manifestProjectedAiUnits,
23
+ manifestModels,
24
+ };
25
+ }
26
+ //# sourceMappingURL=ai-units-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-units-config.js","sourceRoot":"","sources":["../../src/spend/ai-units-config.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,iBAAiB,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAsBxE,MAAM,UAAU,kBAAkB,CAChC,MAA4C,EAC5C,GAAsB;IAEtB,MAAM,EAAE,eAAe,EAAE,cAAc,EAAE,GAAG,uBAAuB,CAAC,GAAG,CAAC,CAAC;IAEzE,MAAM,mBAAmB,GAAiC,EAAE,CAAC;IAC7D,MAAM,wBAAwB,GAAkC,EAAE,CAAC;IACnE,MAAM,cAAc,GAAuC,EAAE,CAAC;IAE9D,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,CAAC;QACjF,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,CAAC,YAAY;YAAE,SAAS;QAC5B,mBAAmB,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC;QAChD,wBAAwB,CAAC,WAAW,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,YAAY,CAAC,CAAC;QACjG,cAAc,CAAC,WAAW,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAC5C,CAAC;IAED,IAAI,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEpE,OAAO;QACL,WAAW,EAAE,eAAe;QAC5B,UAAU,EAAE,cAAc;QAC1B,mBAAmB;QACnB,wBAAwB;QACxB,cAAc;KACf,CAAC;AACJ,CAAC"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * AI-units calibration — the universal cost language for the M1
3
+ * cost-protection throttle (issue #815).
4
+ *
5
+ * One "AI unit" is the marginal USD cost of one task on the
6
+ * GPT-5.4-mini Codex-Plus baseline, scaled so that **100 units = 10% of a
7
+ * 6-hour-block-equivalent USD spend on that baseline**. Every other
8
+ * harness/model converts its USD cost-per-task through the same peg
9
+ * (`USD / GPT_5_4_MINI_USD_PER_BLOCK * 100`).
10
+ *
11
+ * Why a unit-of-account rather than raw USD: the ceiling is the same
12
+ * **100 units per 6h block** for every harness/model, so an operator on
13
+ * Haiku 4.5 (~1 unit/task) gets ~100 tasks per block, while an operator
14
+ * on Opus 4.7 (~50 units/task) gets ~2 tasks per block. The peg keeps the
15
+ * ceiling readable and ties it to the milestone framing of "~10% of a
16
+ * baseline AI subscription's weekly cap" (#605).
17
+ *
18
+ * The cap is layered with the existing per-credential USD `spendCaps`
19
+ * spend-cap gate from PR #345/#346 — both gates run; the more
20
+ * conservative one wins. This module owns the AI-units side only.
21
+ */
22
+ /**
23
+ * Calibration peg: the USD cost of one GPT-5.4-mini Codex-Plus 6h block.
24
+ * Derived from a typical Codex Plus 6h block on GPT-5.4-mini: ~10 tasks at
25
+ * ~$0.0525/task (50k input × $0.00025/1k + 20k output × $0.002/1k) → ~$0.5.
26
+ *
27
+ * Conservative — biases toward over-warning rather than under-warning so
28
+ * the ceiling pauses early rather than late.
29
+ */
30
+ export declare const GPT_5_4_MINI_USD_PER_BLOCK = 0.5;
31
+ /**
32
+ * The default reference ceiling — 100 AI units per 6h UTC-aligned block,
33
+ * with a weekly safety net at 28 × that (one block × 28 blocks/week).
34
+ *
35
+ * Override at runtime with `JINN_AI_UNITS_CEILING_OVERRIDE` (see
36
+ * `resolveReferenceCeiling`). Set as `<units>` for a per-block override
37
+ * (weekly auto-scales to 28×) or `<block>:<week>` for both explicitly.
38
+ */
39
+ export declare const REFERENCE_CEILING: {
40
+ readonly units_per_block: number;
41
+ readonly units_per_week: number;
42
+ };
43
+ /**
44
+ * Project the AI-unit cost of one task for a harness/model combination.
45
+ *
46
+ * - Returns `0` when the credential resolves to a subscription path
47
+ * (`anthropic:subscription`, `openai:subscription`) — the provider's
48
+ * own quota is the cap; the AI-units gate stays out of the way.
49
+ * - Returns `0` for harnesses that make no marginal LLM call
50
+ * (prediction harnesses, evaluators, unknown harnesses).
51
+ * - Returns `null` when the harness IS on a paid-API path but the model
52
+ * is unknown to the cost table — the caller treats that as "no
53
+ * projection available" and surfaces "unavailable" rather than
54
+ * gating on a guess.
55
+ * - Otherwise returns `cost.usd / GPT_5_4_MINI_USD_PER_BLOCK * 100`.
56
+ *
57
+ * `credentialId` is the env-resolved auth path
58
+ * (`anthropic:subscription`, `anthropic:api-key`, `openai:api-key`,
59
+ * `hermes:api-key`, …). When omitted, defaults to "treat the harness as
60
+ * subscription-only" so callers without env access (the SPA panel
61
+ * pre-join) get the safer reading.
62
+ */
63
+ export declare function projectAiUnits(harness: string | undefined, model: string | undefined, credentialId?: string | null): number | null;
64
+ /**
65
+ * Resolve the active ceiling from env. CI / tests override the baked-in
66
+ * 100 units via `JINN_AI_UNITS_CEILING_OVERRIDE`. Accepts:
67
+ *
68
+ * - `"<n>"` — sets `units_per_block = n`; weekly auto-scales to `n * 28`.
69
+ * - `"<block>:<week>"` — sets both explicitly.
70
+ *
71
+ * Falls back to the baked-in `REFERENCE_CEILING` on a missing or
72
+ * malformed value (env-typo must not silently disable the gate).
73
+ */
74
+ export declare function resolveReferenceCeiling(env: NodeJS.ProcessEnv): {
75
+ units_per_block: number;
76
+ units_per_week: number;
77
+ };
78
+ /** Start of the next 6h UTC-aligned block after `now`. */
79
+ export declare function blockResetsAtUtc(now: Date): Date;
80
+ /**
81
+ * Stable id for a 6h block — used to dedupe `ai_units_cap_reached`
82
+ * activity events to one-per-(credential, block).
83
+ */
84
+ export declare function blockIdUtc(now: Date): string;
85
+ /** Reset instant for the 7-day window — `now + (boundary - elapsed)`. We use rolling 7d here. */
86
+ export declare function weekResetsAtUtc(now: Date): Date;
@@ -0,0 +1,163 @@
1
+ /**
2
+ * AI-units calibration — the universal cost language for the M1
3
+ * cost-protection throttle (issue #815).
4
+ *
5
+ * One "AI unit" is the marginal USD cost of one task on the
6
+ * GPT-5.4-mini Codex-Plus baseline, scaled so that **100 units = 10% of a
7
+ * 6-hour-block-equivalent USD spend on that baseline**. Every other
8
+ * harness/model converts its USD cost-per-task through the same peg
9
+ * (`USD / GPT_5_4_MINI_USD_PER_BLOCK * 100`).
10
+ *
11
+ * Why a unit-of-account rather than raw USD: the ceiling is the same
12
+ * **100 units per 6h block** for every harness/model, so an operator on
13
+ * Haiku 4.5 (~1 unit/task) gets ~100 tasks per block, while an operator
14
+ * on Opus 4.7 (~50 units/task) gets ~2 tasks per block. The peg keeps the
15
+ * ceiling readable and ties it to the milestone framing of "~10% of a
16
+ * baseline AI subscription's weekly cap" (#605).
17
+ *
18
+ * The cap is layered with the existing per-credential USD `spendCaps`
19
+ * spend-cap gate from PR #345/#346 — both gates run; the more
20
+ * conservative one wins. This module owns the AI-units side only.
21
+ */
22
+ import { estimateModelCost } from '../harnesses/cost-estimates.js';
23
+ import { CLAUDE_CODE_HARNESS, CODEX_HARNESS, HERMES_AGENT_HARNESS, canonicalHarnessName, } from '../harnesses/names.js';
24
+ /**
25
+ * Calibration peg: the USD cost of one GPT-5.4-mini Codex-Plus 6h block.
26
+ * Derived from a typical Codex Plus 6h block on GPT-5.4-mini: ~10 tasks at
27
+ * ~$0.0525/task (50k input × $0.00025/1k + 20k output × $0.002/1k) → ~$0.5.
28
+ *
29
+ * Conservative — biases toward over-warning rather than under-warning so
30
+ * the ceiling pauses early rather than late.
31
+ */
32
+ export const GPT_5_4_MINI_USD_PER_BLOCK = 0.5;
33
+ /**
34
+ * The default reference ceiling — 100 AI units per 6h UTC-aligned block,
35
+ * with a weekly safety net at 28 × that (one block × 28 blocks/week).
36
+ *
37
+ * Override at runtime with `JINN_AI_UNITS_CEILING_OVERRIDE` (see
38
+ * `resolveReferenceCeiling`). Set as `<units>` for a per-block override
39
+ * (weekly auto-scales to 28×) or `<block>:<week>` for both explicitly.
40
+ */
41
+ export const REFERENCE_CEILING = {
42
+ units_per_block: 100,
43
+ units_per_week: 100 * 28,
44
+ };
45
+ /** 6h block in milliseconds — UTC blocks start at 00:00 / 06:00 / 12:00 / 18:00. */
46
+ const SIX_HOUR_BLOCK_MS = 6 * 60 * 60 * 1_000;
47
+ /** 7 days in milliseconds — UTC-aligned. */
48
+ const SEVEN_DAY_MS = 7 * 24 * 60 * 60 * 1_000;
49
+ /**
50
+ * Project the AI-unit cost of one task for a harness/model combination.
51
+ *
52
+ * - Returns `0` when the credential resolves to a subscription path
53
+ * (`anthropic:subscription`, `openai:subscription`) — the provider's
54
+ * own quota is the cap; the AI-units gate stays out of the way.
55
+ * - Returns `0` for harnesses that make no marginal LLM call
56
+ * (prediction harnesses, evaluators, unknown harnesses).
57
+ * - Returns `null` when the harness IS on a paid-API path but the model
58
+ * is unknown to the cost table — the caller treats that as "no
59
+ * projection available" and surfaces "unavailable" rather than
60
+ * gating on a guess.
61
+ * - Otherwise returns `cost.usd / GPT_5_4_MINI_USD_PER_BLOCK * 100`.
62
+ *
63
+ * `credentialId` is the env-resolved auth path
64
+ * (`anthropic:subscription`, `anthropic:api-key`, `openai:api-key`,
65
+ * `hermes:api-key`, …). When omitted, defaults to "treat the harness as
66
+ * subscription-only" so callers without env access (the SPA panel
67
+ * pre-join) get the safer reading.
68
+ */
69
+ export function projectAiUnits(harness, model, credentialId) {
70
+ if (!harness)
71
+ return null;
72
+ const canonical = canonicalHarnessName(harness);
73
+ const isPaidLlmHarness = canonical === CLAUDE_CODE_HARNESS ||
74
+ canonical === CODEX_HARNESS ||
75
+ canonical === HERMES_AGENT_HARNESS;
76
+ if (!isPaidLlmHarness)
77
+ return 0;
78
+ // Subscription credentials => the provider's quota is the cap.
79
+ if (credentialId && credentialId.endsWith(':subscription'))
80
+ return 0;
81
+ // Paid-API credential (`*:api-key`) or no resolved credential (caller
82
+ // doesn't know): project the model cost. When no credential is
83
+ // resolved AND the harness is claude-code / codex, prefer 0 — that's
84
+ // the "no provider key set" state where no claims will actually run.
85
+ if (!credentialId && (canonical === CLAUDE_CODE_HARNESS || canonical === CODEX_HARNESS)) {
86
+ return 0;
87
+ }
88
+ if (!model)
89
+ return null;
90
+ const cost = estimateModelCost(model);
91
+ if (!cost)
92
+ return null;
93
+ return (cost.usd / GPT_5_4_MINI_USD_PER_BLOCK) * 100;
94
+ }
95
+ /**
96
+ * Resolve the active ceiling from env. CI / tests override the baked-in
97
+ * 100 units via `JINN_AI_UNITS_CEILING_OVERRIDE`. Accepts:
98
+ *
99
+ * - `"<n>"` — sets `units_per_block = n`; weekly auto-scales to `n * 28`.
100
+ * - `"<block>:<week>"` — sets both explicitly.
101
+ *
102
+ * Falls back to the baked-in `REFERENCE_CEILING` on a missing or
103
+ * malformed value (env-typo must not silently disable the gate).
104
+ */
105
+ export function resolveReferenceCeiling(env) {
106
+ const raw = env['JINN_AI_UNITS_CEILING_OVERRIDE'];
107
+ if (raw == null || raw.trim() === '') {
108
+ return { units_per_block: REFERENCE_CEILING.units_per_block, units_per_week: REFERENCE_CEILING.units_per_week };
109
+ }
110
+ const trimmed = raw.trim();
111
+ if (trimmed.includes(':')) {
112
+ const [a, b] = trimmed.split(':', 2);
113
+ const block = Number(a);
114
+ const week = Number(b);
115
+ if (Number.isFinite(block) && block > 0 && Number.isFinite(week) && week > 0) {
116
+ return { units_per_block: block, units_per_week: week };
117
+ }
118
+ warnMalformedOverride(raw);
119
+ return { units_per_block: REFERENCE_CEILING.units_per_block, units_per_week: REFERENCE_CEILING.units_per_week };
120
+ }
121
+ const block = Number(trimmed);
122
+ if (Number.isFinite(block) && block > 0) {
123
+ return { units_per_block: block, units_per_week: block * 28 };
124
+ }
125
+ warnMalformedOverride(raw);
126
+ return { units_per_block: REFERENCE_CEILING.units_per_block, units_per_week: REFERENCE_CEILING.units_per_week };
127
+ }
128
+ function warnMalformedOverride(raw) {
129
+ // One-time warn per resolve call — the function is only invoked at
130
+ // startup, so a module-level memo is unnecessary. Surfaces the operator's
131
+ // typo instead of silently falling back to the baked-in default.
132
+ console.warn(`[ai-units] warn: JINN_AI_UNITS_CEILING_OVERRIDE="${raw}" is malformed — ` +
133
+ `using default ${REFERENCE_CEILING.units_per_block}/${REFERENCE_CEILING.units_per_week} per (block, week)`);
134
+ }
135
+ /**
136
+ * Start of the 6h UTC-aligned block containing `now`. Blocks begin at
137
+ * 00:00, 06:00, 12:00, 18:00 UTC; `blockStartUtc(t)` returns the most
138
+ * recent such instant ≤ t. Used to bound the "units this block" sum.
139
+ */
140
+ function blockStartUtc(now) {
141
+ const startOfDay = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());
142
+ const sinceDayStart = now.getTime() - startOfDay;
143
+ // Cap at 3 — 4 blocks per day (indices 0..3); defensive against
144
+ // millisecond rounding pushing the floor over 4 at day boundaries.
145
+ const blocksIn = Math.min(Math.floor(sinceDayStart / SIX_HOUR_BLOCK_MS), 3);
146
+ return new Date(startOfDay + blocksIn * SIX_HOUR_BLOCK_MS);
147
+ }
148
+ /** Start of the next 6h UTC-aligned block after `now`. */
149
+ export function blockResetsAtUtc(now) {
150
+ return new Date(blockStartUtc(now).getTime() + SIX_HOUR_BLOCK_MS);
151
+ }
152
+ /**
153
+ * Stable id for a 6h block — used to dedupe `ai_units_cap_reached`
154
+ * activity events to one-per-(credential, block).
155
+ */
156
+ export function blockIdUtc(now) {
157
+ return blockStartUtc(now).toISOString();
158
+ }
159
+ /** Reset instant for the 7-day window — `now + (boundary - elapsed)`. We use rolling 7d here. */
160
+ export function weekResetsAtUtc(now) {
161
+ return new Date(now.getTime() + SEVEN_DAY_MS);
162
+ }
163
+ //# sourceMappingURL=ai-units.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-units.js","sourceRoot":"","sources":["../../src/spend/ai-units.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type CredentialId } from './credential.js';
2
+ export interface CostSurfaceHarnessStatus {
3
+ credentialId: CredentialId | null;
4
+ usesPaidApiKey: boolean;
5
+ }
6
+ export interface CostSurfaceStatus {
7
+ harnesses: Record<string, CostSurfaceHarnessStatus>;
8
+ }
9
+ export declare function credentialUsesPaidApiKey(credentialId: CredentialId | null): boolean;
10
+ export declare function buildCostSurfaceStatus(env: NodeJS.ProcessEnv): CostSurfaceStatus;
@@ -0,0 +1,22 @@
1
+ import { CLAUDE_CODE_HARNESS, CODEX_HARNESS, HERMES_AGENT_HARNESS, } from '../harnesses/names.js';
2
+ import { resolveCredentialId } from './credential.js';
3
+ const COST_SURFACE_HARNESSES = [
4
+ CLAUDE_CODE_HARNESS,
5
+ CODEX_HARNESS,
6
+ HERMES_AGENT_HARNESS,
7
+ ];
8
+ export function credentialUsesPaidApiKey(credentialId) {
9
+ return credentialId !== null && credentialId.endsWith(':api-key');
10
+ }
11
+ export function buildCostSurfaceStatus(env) {
12
+ const harnesses = {};
13
+ for (const harness of COST_SURFACE_HARNESSES) {
14
+ const credentialId = resolveCredentialId(harness, env);
15
+ harnesses[harness] = {
16
+ credentialId,
17
+ usesPaidApiKey: credentialUsesPaidApiKey(credentialId),
18
+ };
19
+ }
20
+ return { harnesses };
21
+ }
22
+ //# sourceMappingURL=cost-surface-status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cost-surface-status.js","sourceRoot":"","sources":["../../src/spend/cost-surface-status.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAqB,MAAM,iBAAiB,CAAC;AAEzE,MAAM,sBAAsB,GAAG;IAC7B,mBAAmB;IACnB,aAAa;IACb,oBAAoB;CACZ,CAAC;AAWX,MAAM,UAAU,wBAAwB,CAAC,YAAiC;IACxE,OAAO,YAAY,KAAK,IAAI,IAAI,YAAY,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,GAAsB;IAC3D,MAAM,SAAS,GAA6C,EAAE,CAAC;IAC/D,KAAK,MAAM,OAAO,IAAI,sBAAsB,EAAE,CAAC;QAC7C,MAAM,YAAY,GAAG,mBAAmB,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QACvD,SAAS,CAAC,OAAO,CAAC,GAAG;YACnB,YAAY;YACZ,cAAc,EAAE,wBAAwB,CAAC,YAAY,CAAC;SACvD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,CAAC;AACvB,CAAC"}
@@ -0,0 +1,8 @@
1
+ /** A credential identity. Format: `{provider}:{authMethod}`, e.g. `anthropic:api-key`. Never empty. */
2
+ export type CredentialId = string;
3
+ /**
4
+ * Resolve which authentication credential a harness will bill against, from
5
+ * the presence of provider env vars. Returns null when the harness makes no
6
+ * paid LLM call (e.g. prediction harnesses) or no credential is recognisable.
7
+ */
8
+ export declare function resolveCredentialId(harness: string | undefined, env: NodeJS.ProcessEnv): CredentialId | null;
@@ -0,0 +1,30 @@
1
+ import { CLAUDE_CODE_HARNESS, CODEX_HARNESS, HERMES_AGENT_HARNESS, canonicalHarnessName, } from '../harnesses/names.js';
2
+ /**
3
+ * Resolve which authentication credential a harness will bill against, from
4
+ * the presence of provider env vars. Returns null when the harness makes no
5
+ * paid LLM call (e.g. prediction harnesses) or no credential is recognisable.
6
+ */
7
+ export function resolveCredentialId(harness, env) {
8
+ if (!harness)
9
+ return null;
10
+ switch (canonicalHarnessName(harness)) {
11
+ case CLAUDE_CODE_HARNESS:
12
+ // empty string treated as absent
13
+ if (env['CLAUDE_CODE_OAUTH_TOKEN'])
14
+ return 'anthropic:subscription';
15
+ if (env['ANTHROPIC_API_KEY'])
16
+ return 'anthropic:api-key';
17
+ return null;
18
+ case CODEX_HARNESS:
19
+ if (env['OPENAI_API_KEY'])
20
+ return 'openai:api-key';
21
+ return 'openai:subscription';
22
+ case HERMES_AGENT_HARNESS: {
23
+ const provider = (env['JINN_HERMES_PROVIDER'] ?? 'hermes').trim().toLowerCase();
24
+ return `${provider}:api-key`;
25
+ }
26
+ default:
27
+ return null;
28
+ }
29
+ }
30
+ //# sourceMappingURL=credential.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credential.js","sourceRoot":"","sources":["../../src/spend/credential.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAK/B;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAA2B,EAC3B,GAAsB;IAEtB,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAC1B,QAAQ,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QACtC,KAAK,mBAAmB;YACtB,iCAAiC;YACjC,IAAI,GAAG,CAAC,yBAAyB,CAAC;gBAAE,OAAO,wBAAwB,CAAC;YACpE,IAAI,GAAG,CAAC,mBAAmB,CAAC;gBAAE,OAAO,mBAAmB,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,KAAK,aAAa;YAChB,IAAI,GAAG,CAAC,gBAAgB,CAAC;gBAAE,OAAO,gBAAgB,CAAC;YACnD,OAAO,qBAAqB,CAAC;QAC/B,KAAK,oBAAoB,CAAC,CAAC,CAAC;YAC1B,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAChF,OAAO,GAAG,QAAQ,UAAU,CAAC;QAC/B,CAAC;QACD;YACE,OAAO,IAAI,CAAC;IAChB,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { JinnConfig } from '../config.js';
2
+ import { type CredentialId } from './credential.js';
3
+ export interface SpendCapDaemonConfig {
4
+ /** credentialId -> USD/day cap. */
5
+ caps: Record<CredentialId, number>;
6
+ /** manifest CID -> the credential its harness bills against. */
7
+ manifestCredentials: Record<string, CredentialId>;
8
+ }
9
+ /**
10
+ * Assemble the daemon's spend-cap config from operator config + env. Returns
11
+ * undefined when no credential ends up with a cap (the gate then stays off).
12
+ */
13
+ export declare function buildSpendCapConfig(config: Pick<JinnConfig, 'joinedSolverNets' | 'spendCaps'>, env: NodeJS.ProcessEnv): SpendCapDaemonConfig | undefined;
@@ -0,0 +1,24 @@
1
+ import { resolveCredentialId } from './credential.js';
2
+ /**
3
+ * Assemble the daemon's spend-cap config from operator config + env. Returns
4
+ * undefined when no credential ends up with a cap (the gate then stays off).
5
+ */
6
+ export function buildSpendCapConfig(config, env) {
7
+ const blanketRaw = env['JINN_SPEND_CAP_USD'];
8
+ const blanketNum = blanketRaw != null && blanketRaw.trim() !== '' ? Number(blanketRaw) : NaN;
9
+ const blanket = Number.isFinite(blanketNum) && blanketNum > 0 ? blanketNum : undefined;
10
+ const manifestCredentials = {};
11
+ for (const [manifestCid, entry] of Object.entries(config.joinedSolverNets ?? {})) {
12
+ const credentialId = resolveCredentialId(entry.harness, env);
13
+ if (credentialId)
14
+ manifestCredentials[manifestCid] = credentialId;
15
+ }
16
+ const caps = {};
17
+ for (const credentialId of new Set(Object.values(manifestCredentials))) {
18
+ const cap = config.spendCaps?.[credentialId] ?? blanket;
19
+ if (cap != null)
20
+ caps[credentialId] = cap;
21
+ }
22
+ return Object.keys(caps).length > 0 ? { caps, manifestCredentials } : undefined;
23
+ }
24
+ //# sourceMappingURL=daemon-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon-config.js","sourceRoot":"","sources":["../../src/spend/daemon-config.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAqB,MAAM,iBAAiB,CAAC;AASzE;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA0D,EAC1D,GAAsB;IAEtB,MAAM,UAAU,GAAG,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC7C,MAAM,UAAU,GAAG,UAAU,IAAI,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAC7F,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;IAEvF,MAAM,mBAAmB,GAAiC,EAAE,CAAC;IAC7D,KAAK,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,IAAI,EAAE,CAAC,EAAE,CAAC;QACjF,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,IAAI,YAAY;YAAE,mBAAmB,CAAC,WAAW,CAAC,GAAG,YAAY,CAAC;IACpE,CAAC;IAED,MAAM,IAAI,GAAiC,EAAE,CAAC;IAC9C,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC,EAAE,CAAC;QACvE,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC;QACxD,IAAI,GAAG,IAAI,IAAI;YAAE,IAAI,CAAC,YAAY,CAAC,GAAG,GAAG,CAAC;IAC5C,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAClF,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Price a token count in USD for the given model. Returns null when the model
3
+ * is unknown to the catalog (caller falls back to a heuristic).
4
+ *
5
+ * Uses the tokenlens bundled, offline model catalog (models.dev snapshot).
6
+ * The `tokenlens` re-export of `getTokenCosts` accepts an object with
7
+ * `{ modelId, usage }` and injects the default catalog automatically.
8
+ *
9
+ * Note: tokenlens's bundled catalog may lag current model IDs (e.g.
10
+ * `claude-haiku-4-5-20251001` is absent in v1.3.1 and returns null);
11
+ * callers must handle null gracefully.
12
+ */
13
+ export declare function priceTokens(modelId: string, tokens: {
14
+ inputTokens: number;
15
+ outputTokens: number;
16
+ }): number | null;
@@ -0,0 +1,26 @@
1
+ import { getTokenCosts } from 'tokenlens';
2
+ /**
3
+ * Price a token count in USD for the given model. Returns null when the model
4
+ * is unknown to the catalog (caller falls back to a heuristic).
5
+ *
6
+ * Uses the tokenlens bundled, offline model catalog (models.dev snapshot).
7
+ * The `tokenlens` re-export of `getTokenCosts` accepts an object with
8
+ * `{ modelId, usage }` and injects the default catalog automatically.
9
+ *
10
+ * Note: tokenlens's bundled catalog may lag current model IDs (e.g.
11
+ * `claude-haiku-4-5-20251001` is absent in v1.3.1 and returns null);
12
+ * callers must handle null gracefully.
13
+ */
14
+ export function priceTokens(modelId, tokens) {
15
+ try {
16
+ const costs = getTokenCosts({
17
+ modelId,
18
+ usage: { prompt_tokens: tokens.inputTokens, completion_tokens: tokens.outputTokens },
19
+ });
20
+ return typeof costs?.totalUSD === 'number' ? costs.totalUSD : null;
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ }
26
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/spend/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,WAAW,CACzB,OAAe,EACf,MAAqD;IAErD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,aAAa,CAAC;YAC1B,OAAO;YACP,KAAK,EAAE,EAAE,aAAa,EAAE,MAAM,CAAC,WAAW,EAAE,iBAAiB,EAAE,MAAM,CAAC,YAAY,EAAE;SACrF,CAAC,CAAC;QACH,OAAO,OAAO,KAAK,EAAE,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IACrE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { Store } from '../store/store.js';
2
+ /**
3
+ * Record the cost of one finished harness run as a `task_cost` activity row.
4
+ * Called once per harness run (at the POST_SNAPSHOT transition). Best-effort:
5
+ * never throws — a parse failure must not break task execution.
6
+ */
7
+ export declare function recordTaskCost(store: Store, args: {
8
+ requestId: string;
9
+ harness: string;
10
+ model: string | undefined;
11
+ workingDir: string;
12
+ solverType: string | null;
13
+ }, env?: NodeJS.ProcessEnv): void;
@@ -0,0 +1,36 @@
1
+ import { resolveCredentialId } from './credential.js';
2
+ import { harvestHarnessUsage } from './usage.js';
3
+ /**
4
+ * Record the cost of one finished harness run as a `task_cost` activity row.
5
+ * Called once per harness run (at the POST_SNAPSHOT transition). Best-effort:
6
+ * never throws — a parse failure must not break task execution.
7
+ */
8
+ export function recordTaskCost(store, args, env = process.env) {
9
+ try {
10
+ const credentialId = resolveCredentialId(args.harness, env);
11
+ if (!credentialId)
12
+ return;
13
+ const usage = harvestHarnessUsage(args.harness, args.workingDir, args.model);
14
+ const actualCostUsdMicros = Math.round(usage.costUsd * 1_000_000);
15
+ store.recordActivityEvent({
16
+ ts: new Date().toISOString(),
17
+ kind: 'task_cost',
18
+ requestId: args.requestId,
19
+ solverType: args.solverType,
20
+ credentialId,
21
+ costUsdMicros: actualCostUsdMicros,
22
+ model: usage.model,
23
+ detail: usage.estimated ? 'estimated' : 'observed',
24
+ });
25
+ // Issue #815: fill actual_cost_usd_micros on the per-request claimed
26
+ // row + set claim_status='delivered'. ai_units stays as captured at
27
+ // claim time (estimates are the gate input, never recomputed).
28
+ // Idempotent: no-op when no claimed row exists for this request id.
29
+ store.finalizeClaimDelivered(args.requestId, actualCostUsdMicros);
30
+ }
31
+ catch (err) {
32
+ console.warn(`[spend] failed to record task cost for ${args.requestId}: ` +
33
+ `${err instanceof Error ? err.message : String(err)}`);
34
+ }
35
+ }
36
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../src/spend/record.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAY,EACZ,IAMC,EACD,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;QAC5D,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7E,MAAM,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAAC;QAClE,KAAK,CAAC,mBAAmB,CAAC;YACxB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,YAAY;YACZ,aAAa,EAAE,mBAAmB;YAClC,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,MAAM,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU;SACnD,CAAC,CAAC;QACH,qEAAqE;QACrE,oEAAoE;QACpE,+DAA+D;QAC/D,oEAAoE;QACpE,KAAK,CAAC,sBAAsB,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;IACpE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CACV,0CAA0C,IAAI,CAAC,SAAS,IAAI;YAC1D,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACxD,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ /** USD attributed to a task whose model has no known price. */
2
+ export declare const UNKNOWN_MODEL_FALLBACK_USD = 1;
3
+ export interface HarnessUsage {
4
+ model: string;
5
+ costUsd: number;
6
+ /** true = derived from an a-priori heuristic; false = from observed usage. */
7
+ estimated: boolean;
8
+ inputTokens?: number;
9
+ outputTokens?: number;
10
+ }
11
+ /** Parse Claude Code `--output-format stream-json` output for the terminal result. */
12
+ export declare function parseClaudeCodeUsage(stdoutJsonl: string): {
13
+ costUsd: number;
14
+ inputTokens?: number;
15
+ outputTokens?: number;
16
+ } | null;
17
+ /** Parse Codex `--json` output for the last turn.completed token usage. */
18
+ export declare function parseCodexUsage(stdoutJsonl: string): {
19
+ inputTokens: number;
20
+ outputTokens: number;
21
+ } | null;
22
+ /**
23
+ * Determine the USD cost of a finished harness run. Reads the harness's own
24
+ * output file for observed usage; falls back to a heuristic on any failure.
25
+ * Always returns a HarnessUsage — never throws.
26
+ */
27
+ export declare function harvestHarnessUsage(harness: string, workingDir: string, model: string | undefined): HarnessUsage;