@miller-tech/uap 2.21.3 → 2.36.1

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 (328) hide show
  1. package/README.md +22 -10
  2. package/config/model-profiles/qwen38-coder390.json +46 -0
  3. package/config/model-profiles/qwen38.json +3 -2
  4. package/config/model-registry.json +110 -0
  5. package/dist/.tsbuildinfo +1 -1
  6. package/dist/benchmarks/paired/types.d.ts +2 -2
  7. package/dist/bin/cli.js +315 -20
  8. package/dist/bin/cli.js.map +1 -1
  9. package/dist/cli/attention.d.ts +6 -0
  10. package/dist/cli/attention.d.ts.map +1 -0
  11. package/dist/cli/attention.js +115 -0
  12. package/dist/cli/attention.js.map +1 -0
  13. package/dist/cli/bench-report.d.ts +82 -0
  14. package/dist/cli/bench-report.d.ts.map +1 -0
  15. package/dist/cli/bench-report.js +237 -0
  16. package/dist/cli/bench-report.js.map +1 -0
  17. package/dist/cli/clients.d.ts +6 -0
  18. package/dist/cli/clients.d.ts.map +1 -0
  19. package/dist/cli/clients.js +142 -0
  20. package/dist/cli/clients.js.map +1 -0
  21. package/dist/cli/dashboard-grants.d.ts +26 -0
  22. package/dist/cli/dashboard-grants.d.ts.map +1 -0
  23. package/dist/cli/dashboard-grants.js +69 -0
  24. package/dist/cli/dashboard-grants.js.map +1 -0
  25. package/dist/cli/dashboard-pace.d.ts +31 -0
  26. package/dist/cli/dashboard-pace.d.ts.map +1 -0
  27. package/dist/cli/dashboard-pace.js +42 -0
  28. package/dist/cli/dashboard-pace.js.map +1 -0
  29. package/dist/cli/dashboard-session.d.ts +25 -0
  30. package/dist/cli/dashboard-session.d.ts.map +1 -0
  31. package/dist/cli/dashboard-session.js +814 -0
  32. package/dist/cli/dashboard-session.js.map +1 -0
  33. package/dist/cli/dashboard-types.d.ts +18 -0
  34. package/dist/cli/dashboard-types.d.ts.map +1 -0
  35. package/dist/cli/dashboard-types.js +9 -0
  36. package/dist/cli/dashboard-types.js.map +1 -0
  37. package/dist/cli/dashboard.d.ts +2 -18
  38. package/dist/cli/dashboard.d.ts.map +1 -1
  39. package/dist/cli/dashboard.js +43 -793
  40. package/dist/cli/dashboard.js.map +1 -1
  41. package/dist/cli/deliver.d.ts +35 -0
  42. package/dist/cli/deliver.d.ts.map +1 -1
  43. package/dist/cli/deliver.js +93 -9
  44. package/dist/cli/deliver.js.map +1 -1
  45. package/dist/cli/diff.d.ts +85 -0
  46. package/dist/cli/diff.d.ts.map +1 -0
  47. package/dist/cli/diff.js +293 -0
  48. package/dist/cli/diff.js.map +1 -0
  49. package/dist/cli/eta.d.ts +35 -0
  50. package/dist/cli/eta.d.ts.map +1 -0
  51. package/dist/cli/eta.js +101 -0
  52. package/dist/cli/eta.js.map +1 -0
  53. package/dist/cli/handsfree.d.ts +7 -0
  54. package/dist/cli/handsfree.d.ts.map +1 -1
  55. package/dist/cli/handsfree.js +27 -0
  56. package/dist/cli/handsfree.js.map +1 -1
  57. package/dist/cli/hooks.d.ts.map +1 -1
  58. package/dist/cli/hooks.js +126 -5
  59. package/dist/cli/hooks.js.map +1 -1
  60. package/dist/cli/loops.d.ts +24 -5
  61. package/dist/cli/loops.d.ts.map +1 -1
  62. package/dist/cli/loops.js +92 -12
  63. package/dist/cli/loops.js.map +1 -1
  64. package/dist/cli/models.d.ts +62 -0
  65. package/dist/cli/models.d.ts.map +1 -0
  66. package/dist/cli/models.js +567 -0
  67. package/dist/cli/models.js.map +1 -0
  68. package/dist/cli/plan.d.ts +9 -0
  69. package/dist/cli/plan.d.ts.map +1 -1
  70. package/dist/cli/plan.js +57 -4
  71. package/dist/cli/plan.js.map +1 -1
  72. package/dist/cli/proxy-relay.d.ts +55 -0
  73. package/dist/cli/proxy-relay.d.ts.map +1 -0
  74. package/dist/cli/proxy-relay.js +116 -0
  75. package/dist/cli/proxy-relay.js.map +1 -0
  76. package/dist/cli/proxy.d.ts +4 -0
  77. package/dist/cli/proxy.d.ts.map +1 -1
  78. package/dist/cli/proxy.js +26 -1
  79. package/dist/cli/proxy.js.map +1 -1
  80. package/dist/cli/sandbox.d.ts +101 -4
  81. package/dist/cli/sandbox.d.ts.map +1 -1
  82. package/dist/cli/sandbox.js +393 -44
  83. package/dist/cli/sandbox.js.map +1 -1
  84. package/dist/cli/self.d.ts +13 -0
  85. package/dist/cli/self.d.ts.map +1 -0
  86. package/dist/cli/self.js +94 -0
  87. package/dist/cli/self.js.map +1 -0
  88. package/dist/cli/systemd-services.d.ts +2 -8
  89. package/dist/cli/systemd-services.d.ts.map +1 -1
  90. package/dist/cli/systemd-services.js +5 -20
  91. package/dist/cli/systemd-services.js.map +1 -1
  92. package/dist/cli/task.js +1 -1
  93. package/dist/cli/task.js.map +1 -1
  94. package/dist/cli/wizard-config.d.ts.map +1 -1
  95. package/dist/cli/wizard-config.js +31 -2
  96. package/dist/cli/wizard-config.js.map +1 -1
  97. package/dist/config/settings-registry.d.ts +1 -1
  98. package/dist/config/settings-registry.d.ts.map +1 -1
  99. package/dist/config/settings-registry.js +108 -0
  100. package/dist/config/settings-registry.js.map +1 -1
  101. package/dist/coordination/capability-router.js +2 -2
  102. package/dist/coordination/capability-router.js.map +1 -1
  103. package/dist/coordination/database.d.ts +13 -0
  104. package/dist/coordination/database.d.ts.map +1 -1
  105. package/dist/coordination/database.js +63 -7
  106. package/dist/coordination/database.js.map +1 -1
  107. package/dist/coordination/eta-inject.d.ts +32 -0
  108. package/dist/coordination/eta-inject.d.ts.map +1 -0
  109. package/dist/coordination/eta-inject.js +64 -0
  110. package/dist/coordination/eta-inject.js.map +1 -0
  111. package/dist/coordination/reactor.d.ts.map +1 -1
  112. package/dist/coordination/reactor.js +14 -0
  113. package/dist/coordination/reactor.js.map +1 -1
  114. package/dist/coordination/service.d.ts +18 -8
  115. package/dist/coordination/service.d.ts.map +1 -1
  116. package/dist/coordination/service.js +38 -28
  117. package/dist/coordination/service.js.map +1 -1
  118. package/dist/dashboard/client-registry.d.ts +163 -0
  119. package/dist/dashboard/client-registry.d.ts.map +1 -0
  120. package/dist/dashboard/client-registry.js +549 -0
  121. package/dist/dashboard/client-registry.js.map +1 -0
  122. package/dist/dashboard/controls.d.ts.map +1 -1
  123. package/dist/dashboard/controls.js +33 -4
  124. package/dist/dashboard/controls.js.map +1 -1
  125. package/dist/dashboard/data-seeder.d.ts +10 -7
  126. package/dist/dashboard/data-seeder.d.ts.map +1 -1
  127. package/dist/dashboard/data-seeder.js +31 -75
  128. package/dist/dashboard/data-seeder.js.map +1 -1
  129. package/dist/dashboard/data-service.d.ts +9 -0
  130. package/dist/dashboard/data-service.d.ts.map +1 -1
  131. package/dist/dashboard/data-service.js +32 -3
  132. package/dist/dashboard/data-service.js.map +1 -1
  133. package/dist/dashboard/placement-routes.d.ts +119 -0
  134. package/dist/dashboard/placement-routes.d.ts.map +1 -0
  135. package/dist/dashboard/placement-routes.js +331 -0
  136. package/dist/dashboard/placement-routes.js.map +1 -0
  137. package/dist/dashboard/server.d.ts.map +1 -1
  138. package/dist/dashboard/server.js +408 -2
  139. package/dist/dashboard/server.js.map +1 -1
  140. package/dist/delivery/attention.d.ts +130 -0
  141. package/dist/delivery/attention.d.ts.map +1 -0
  142. package/dist/delivery/attention.js +341 -0
  143. package/dist/delivery/attention.js.map +1 -0
  144. package/dist/delivery/decompose.d.ts +18 -0
  145. package/dist/delivery/decompose.d.ts.map +1 -1
  146. package/dist/delivery/decompose.js +22 -1
  147. package/dist/delivery/decompose.js.map +1 -1
  148. package/dist/delivery/eta-estimate.d.ts +111 -0
  149. package/dist/delivery/eta-estimate.d.ts.map +1 -0
  150. package/dist/delivery/eta-estimate.js +171 -0
  151. package/dist/delivery/eta-estimate.js.map +1 -0
  152. package/dist/delivery/eta.d.ts +23 -0
  153. package/dist/delivery/eta.d.ts.map +1 -0
  154. package/dist/delivery/eta.js +59 -0
  155. package/dist/delivery/eta.js.map +1 -0
  156. package/dist/delivery/orchestrated-mission.d.ts +10 -0
  157. package/dist/delivery/orchestrated-mission.d.ts.map +1 -1
  158. package/dist/delivery/orchestrated-mission.js +15 -1
  159. package/dist/delivery/orchestrated-mission.js.map +1 -1
  160. package/dist/delivery/plan-check.d.ts +7 -1
  161. package/dist/delivery/plan-check.d.ts.map +1 -1
  162. package/dist/delivery/plan-check.js +23 -2
  163. package/dist/delivery/plan-check.js.map +1 -1
  164. package/dist/delivery/run-state.d.ts +1 -1
  165. package/dist/delivery/run-state.d.ts.map +1 -1
  166. package/dist/delivery/run-state.js +8 -1
  167. package/dist/delivery/run-state.js.map +1 -1
  168. package/dist/delivery/task-orchestrator.d.ts +8 -3
  169. package/dist/delivery/task-orchestrator.d.ts.map +1 -1
  170. package/dist/delivery/task-orchestrator.js +75 -43
  171. package/dist/delivery/task-orchestrator.js.map +1 -1
  172. package/dist/delivery/task-sync.d.ts +21 -0
  173. package/dist/delivery/task-sync.d.ts.map +1 -1
  174. package/dist/delivery/task-sync.js +44 -8
  175. package/dist/delivery/task-sync.js.map +1 -1
  176. package/dist/delivery/task-workspace.d.ts +3 -3
  177. package/dist/delivery/task-workspace.js +3 -3
  178. package/dist/generators/self-card.d.ts +65 -0
  179. package/dist/generators/self-card.d.ts.map +1 -0
  180. package/dist/generators/self-card.js +256 -0
  181. package/dist/generators/self-card.js.map +1 -0
  182. package/dist/inference/probe.js +1 -1
  183. package/dist/inference/probe.js.map +1 -1
  184. package/dist/memory/prepopulate.d.ts.map +1 -1
  185. package/dist/memory/prepopulate.js +2 -0
  186. package/dist/memory/prepopulate.js.map +1 -1
  187. package/dist/models/analytics.d.ts +32 -0
  188. package/dist/models/analytics.d.ts.map +1 -1
  189. package/dist/models/analytics.js +68 -5
  190. package/dist/models/analytics.js.map +1 -1
  191. package/dist/models/plan-readability.d.ts +55 -0
  192. package/dist/models/plan-readability.d.ts.map +1 -0
  193. package/dist/models/plan-readability.js +339 -0
  194. package/dist/models/plan-readability.js.map +1 -0
  195. package/dist/placement/admission.d.ts +59 -0
  196. package/dist/placement/admission.d.ts.map +1 -0
  197. package/dist/placement/admission.js +197 -0
  198. package/dist/placement/admission.js.map +1 -0
  199. package/dist/placement/auto.d.ts +44 -0
  200. package/dist/placement/auto.d.ts.map +1 -0
  201. package/dist/placement/auto.js +212 -0
  202. package/dist/placement/auto.js.map +1 -0
  203. package/dist/placement/enforce.d.ts +128 -0
  204. package/dist/placement/enforce.d.ts.map +1 -0
  205. package/dist/placement/enforce.js +655 -0
  206. package/dist/placement/enforce.js.map +1 -0
  207. package/dist/placement/idle.d.ts +50 -0
  208. package/dist/placement/idle.d.ts.map +1 -0
  209. package/dist/placement/idle.js +213 -0
  210. package/dist/placement/idle.js.map +1 -0
  211. package/dist/placement/ledger.d.ts +323 -0
  212. package/dist/placement/ledger.d.ts.map +1 -0
  213. package/dist/placement/ledger.js +263 -0
  214. package/dist/placement/ledger.js.map +1 -0
  215. package/dist/placement/preview.d.ts +51 -0
  216. package/dist/placement/preview.d.ts.map +1 -0
  217. package/dist/placement/preview.js +61 -0
  218. package/dist/placement/preview.js.map +1 -0
  219. package/dist/placement/probes.d.ts +47 -0
  220. package/dist/placement/probes.d.ts.map +1 -0
  221. package/dist/placement/probes.js +207 -0
  222. package/dist/placement/probes.js.map +1 -0
  223. package/dist/placement/proxy-env.d.ts +21 -0
  224. package/dist/placement/proxy-env.d.ts.map +1 -0
  225. package/dist/placement/proxy-env.js +42 -0
  226. package/dist/placement/proxy-env.js.map +1 -0
  227. package/dist/placement/registry.d.ts +1038 -0
  228. package/dist/placement/registry.d.ts.map +1 -0
  229. package/dist/placement/registry.js +274 -0
  230. package/dist/placement/registry.js.map +1 -0
  231. package/dist/policies/liveness.d.ts +6 -8
  232. package/dist/policies/liveness.d.ts.map +1 -1
  233. package/dist/policies/liveness.js +6 -25
  234. package/dist/policies/liveness.js.map +1 -1
  235. package/dist/policies/sandbox-anchor.d.ts +91 -0
  236. package/dist/policies/sandbox-anchor.d.ts.map +1 -0
  237. package/dist/policies/sandbox-anchor.js +187 -0
  238. package/dist/policies/sandbox-anchor.js.map +1 -0
  239. package/dist/policies/sandbox-grants.d.ts +131 -0
  240. package/dist/policies/sandbox-grants.d.ts.map +1 -0
  241. package/dist/policies/sandbox-grants.js +562 -0
  242. package/dist/policies/sandbox-grants.js.map +1 -0
  243. package/dist/state/manifest.d.ts +4 -2
  244. package/dist/state/manifest.d.ts.map +1 -1
  245. package/dist/state/manifest.js +32 -16
  246. package/dist/state/manifest.js.map +1 -1
  247. package/dist/utils/checkout-root.d.ts +2 -0
  248. package/dist/utils/checkout-root.d.ts.map +1 -0
  249. package/dist/utils/checkout-root.js +43 -0
  250. package/dist/utils/checkout-root.js.map +1 -0
  251. package/dist/utils/resolve-uap-bin.d.ts +5 -0
  252. package/dist/utils/resolve-uap-bin.d.ts.map +1 -0
  253. package/dist/utils/resolve-uap-bin.js +33 -0
  254. package/dist/utils/resolve-uap-bin.js.map +1 -0
  255. package/dist/utils/terminal-safe.d.ts +12 -0
  256. package/dist/utils/terminal-safe.d.ts.map +1 -0
  257. package/dist/utils/terminal-safe.js +53 -0
  258. package/dist/utils/terminal-safe.js.map +1 -0
  259. package/docs/ATTRIBUTION.md +1 -0
  260. package/docs/DASHBOARD_UPLIFT_SPEC.md +8 -8
  261. package/docs/INDEX.md +6 -5
  262. package/docs/architecture/OVERVIEW.md +15 -3
  263. package/docs/architecture/PROTOCOL.md +7 -1
  264. package/docs/architecture/adr/0006-operator-directed-model-placement.md +86 -0
  265. package/docs/architecture/adr/0007-auto-displacement-allowlist-as-standing-consent.md +79 -0
  266. package/docs/architecture/adr/0008-fleet-clients-link-out.md +92 -0
  267. package/docs/architecture/adr/0009-completion-triggered-dispatch-and-per-leaf-model-dials.md +71 -0
  268. package/docs/architecture/adr/0010-attention-queue-and-afk-auto-decider.md +159 -0
  269. package/docs/architecture/adr/0011-sandbox-grants-and-network-egress-boundary.md +219 -0
  270. package/docs/architecture/adr/0012-operator-legibility-surfaces.md +175 -0
  271. package/docs/getting-started/INSTALLATION.md +1 -1
  272. package/docs/guides/AUTOMATIC_FEATURES.md +8 -0
  273. package/docs/guides/LOCAL_MODELS.md +1 -1
  274. package/docs/guides/PROXY.md +2 -1
  275. package/docs/guides/SANDBOX.md +91 -4
  276. package/docs/integrations/MCP_ROUTER.md +1 -1
  277. package/docs/plans/model-performance-proof-options.md +176 -0
  278. package/docs/reference/API.md +1 -1
  279. package/docs/reference/CLI.md +169 -4
  280. package/docs/reference/COMPARISON.md +1 -1
  281. package/docs/reference/CONFIGURATION.md +12 -3
  282. package/docs/reference/CONFIGURATION_REFERENCE.md +269 -14
  283. package/docs/reference/DATABASE_SCHEMA.md +1 -1
  284. package/docs/reference/FEATURES.md +15 -3
  285. package/docs/reference/PATTERNS.md +1 -1
  286. package/docs/reference/PLATFORMS.md +14 -1
  287. package/docs/specs/operator-model-placement.md +1314 -0
  288. package/docs/specs/operator-model-placement.review.md +339 -0
  289. package/package.json +2 -2
  290. package/scripts/maintenance/publish-npm.sh +23 -11
  291. package/scripts/version-bump.sh +6 -51
  292. package/src/policies/enforcers/__pycache__/_common.cpython-311.pyc +0 -0
  293. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-311.pyc +0 -0
  294. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-311.pyc +0 -0
  295. package/src/policies/schemas/policies/mandatory-testing-deployment.md +2 -5
  296. package/templates/hooks/__pycache__/deliver_autoroute.cpython-311.pyc +0 -0
  297. package/templates/hooks/session-start.sh +44 -0
  298. package/tools/agents/UAP/cli.py +14 -3
  299. package/tools/agents/UAP/paths.py +50 -0
  300. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-311.pyc +0 -0
  301. package/tools/agents/scripts/anthropic_proxy.py +1386 -99
  302. package/tools/agents/scripts/toolcall_path_normalizer.py +46 -1
  303. package/tools/agents/tests/test_anthropic_proxy_streaming.py +29 -6
  304. package/tools/agents/tests/test_doubling_break.py +64 -5
  305. package/tools/agents/tests/test_error_loop_break.py +10 -4
  306. package/tools/agents/tests/test_error_loop_ignores_correctives.py +13 -2
  307. package/tools/agents/tests/test_lockstep_escalation.py +280 -9
  308. package/tools/agents/tests/test_path_containment.py +94 -0
  309. package/tools/agents/tests/test_proxy_unix_socket.py +162 -0
  310. package/tools/agents/tests/test_signature_quality.py +349 -0
  311. package/tools/agents/tests/test_uap_compliance.py +11 -1
  312. package/web/dash/core.js +1 -0
  313. package/web/dash/tab-clients.js +196 -0
  314. package/web/dash/tab-models.js +387 -8
  315. package/web/dash/tab-policies.js +87 -4
  316. package/web/dash/tabs.js +77 -73
  317. package/web/dashboard.html +6 -0
  318. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  319. package/src/policies/enforcers/__pycache__/expert_review_required.cpython-312.pyc +0 -0
  320. package/src/policies/enforcers/__pycache__/quality_metrics_gate.cpython-312.pyc +0 -0
  321. package/templates/hooks/__pycache__/deliver_autoroute.cpython-312.pyc +0 -0
  322. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  323. package/web/dash/tab-agents.js +0 -21
  324. package/web/dash/tab-deliver.js +0 -12
  325. package/web/dash/tab-memory.js +0 -12
  326. package/web/dash/tab-orchestration.js +0 -21
  327. package/web/dash/tab-overview.js +0 -21
  328. package/web/dash/tab-tasks.js +0 -21
@@ -0,0 +1,323 @@
1
+ import { z } from 'zod';
2
+ import { type ModelRegistry } from './registry.js';
3
+ export declare const LEDGER_VERSION = 1;
4
+ export declare const LedgerDeviceStateSchema: z.ZodObject<{
5
+ kind: z.ZodEnum<["gpu", "cpu"]>;
6
+ total_mib: z.ZodOptional<z.ZodNumber>;
7
+ free_mib: z.ZodOptional<z.ZodNumber>;
8
+ reserved_mib: z.ZodOptional<z.ZodNumber>;
9
+ source: z.ZodOptional<z.ZodString>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ kind: "cpu" | "gpu";
12
+ source?: string | undefined;
13
+ total_mib?: number | undefined;
14
+ reserved_mib?: number | undefined;
15
+ free_mib?: number | undefined;
16
+ }, {
17
+ kind: "cpu" | "gpu";
18
+ source?: string | undefined;
19
+ total_mib?: number | undefined;
20
+ reserved_mib?: number | undefined;
21
+ free_mib?: number | undefined;
22
+ }>;
23
+ export type LedgerDeviceState = z.infer<typeof LedgerDeviceStateSchema>;
24
+ export declare const ResidentStateSchema: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
25
+ export type ResidentState = z.infer<typeof ResidentStateSchema>;
26
+ export declare const LedgerResidentSchema: z.ZodObject<{
27
+ model: z.ZodString;
28
+ config: z.ZodString;
29
+ device: z.ZodString;
30
+ engine: z.ZodOptional<z.ZodString>;
31
+ endpoint: z.ZodOptional<z.ZodString>;
32
+ unit: z.ZodOptional<z.ZodString>;
33
+ state: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
34
+ gpu_mib: z.ZodOptional<z.ZodNumber>;
35
+ host_rss_mib: z.ZodOptional<z.ZodNumber>;
36
+ holders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
37
+ since: z.ZodString;
38
+ }, "strip", z.ZodTypeAny, {
39
+ model: string;
40
+ config: string;
41
+ since: string;
42
+ device: string;
43
+ state: "hot" | "warming" | "paused" | "draining";
44
+ holders: string[];
45
+ endpoint?: string | undefined;
46
+ engine?: string | undefined;
47
+ unit?: string | undefined;
48
+ host_rss_mib?: number | undefined;
49
+ gpu_mib?: number | undefined;
50
+ }, {
51
+ model: string;
52
+ config: string;
53
+ since: string;
54
+ device: string;
55
+ state: "hot" | "warming" | "paused" | "draining";
56
+ endpoint?: string | undefined;
57
+ engine?: string | undefined;
58
+ unit?: string | undefined;
59
+ host_rss_mib?: number | undefined;
60
+ gpu_mib?: number | undefined;
61
+ holders?: string[] | undefined;
62
+ }>;
63
+ export type LedgerResident = z.infer<typeof LedgerResidentSchema>;
64
+ export declare const LedgerPendingSchema: z.ZodObject<{
65
+ id: z.ZodString;
66
+ requested_model: z.ZodString;
67
+ client: z.ZodOptional<z.ZodString>;
68
+ session: z.ZodOptional<z.ZodString>;
69
+ pid: z.ZodOptional<z.ZodNumber>;
70
+ created_at: z.ZodString;
71
+ expires_at: z.ZodString;
72
+ reason: z.ZodOptional<z.ZodString>;
73
+ /** The request's context need in cells (tokens + generation headroom),
74
+ * as estimated by the proxy when it parked the request. Option math is
75
+ * per-request (spec §4.4): cost(config, cells) — without this, the
76
+ * operator's `apply` and the pending list would recompute options
77
+ * against the 32k default instead of what the parked client needs.
78
+ * Absent on entries parked by older proxies → the default applies. */
79
+ cells: z.ZodOptional<z.ZodNumber>;
80
+ }, "strip", z.ZodTypeAny, {
81
+ id: string;
82
+ created_at: string;
83
+ requested_model: string;
84
+ expires_at: string;
85
+ reason?: string | undefined;
86
+ client?: string | undefined;
87
+ session?: string | undefined;
88
+ pid?: number | undefined;
89
+ cells?: number | undefined;
90
+ }, {
91
+ id: string;
92
+ created_at: string;
93
+ requested_model: string;
94
+ expires_at: string;
95
+ reason?: string | undefined;
96
+ client?: string | undefined;
97
+ session?: string | undefined;
98
+ pid?: number | undefined;
99
+ cells?: number | undefined;
100
+ }>;
101
+ export type LedgerPending = z.infer<typeof LedgerPendingSchema>;
102
+ /** Canonical placement TARGET identity for a resident: `device:endpoint`
103
+ * (spec §5.1 — the placement identity, never the model name). One composer,
104
+ * one spelling: the dashboard's admit answer, the proxy's per-target
105
+ * semaphore budget, and (phase 3) coordination leases must key off the
106
+ * identical string, so it is exported rather than re-derived per caller. */
107
+ export declare function residentTargetId(resident: LedgerResident): string;
108
+ export declare const PlacementLedgerSchema: z.ZodObject<{
109
+ version: z.ZodLiteral<1>;
110
+ updated_at: z.ZodOptional<z.ZodString>;
111
+ devices: z.ZodRecord<z.ZodString, z.ZodObject<{
112
+ kind: z.ZodEnum<["gpu", "cpu"]>;
113
+ total_mib: z.ZodOptional<z.ZodNumber>;
114
+ free_mib: z.ZodOptional<z.ZodNumber>;
115
+ reserved_mib: z.ZodOptional<z.ZodNumber>;
116
+ source: z.ZodOptional<z.ZodString>;
117
+ }, "strip", z.ZodTypeAny, {
118
+ kind: "cpu" | "gpu";
119
+ source?: string | undefined;
120
+ total_mib?: number | undefined;
121
+ reserved_mib?: number | undefined;
122
+ free_mib?: number | undefined;
123
+ }, {
124
+ kind: "cpu" | "gpu";
125
+ source?: string | undefined;
126
+ total_mib?: number | undefined;
127
+ reserved_mib?: number | undefined;
128
+ free_mib?: number | undefined;
129
+ }>>;
130
+ residents: z.ZodArray<z.ZodObject<{
131
+ model: z.ZodString;
132
+ config: z.ZodString;
133
+ device: z.ZodString;
134
+ engine: z.ZodOptional<z.ZodString>;
135
+ endpoint: z.ZodOptional<z.ZodString>;
136
+ unit: z.ZodOptional<z.ZodString>;
137
+ state: z.ZodEnum<["hot", "warming", "paused", "draining"]>;
138
+ gpu_mib: z.ZodOptional<z.ZodNumber>;
139
+ host_rss_mib: z.ZodOptional<z.ZodNumber>;
140
+ holders: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
141
+ since: z.ZodString;
142
+ }, "strip", z.ZodTypeAny, {
143
+ model: string;
144
+ config: string;
145
+ since: string;
146
+ device: string;
147
+ state: "hot" | "warming" | "paused" | "draining";
148
+ holders: string[];
149
+ endpoint?: string | undefined;
150
+ engine?: string | undefined;
151
+ unit?: string | undefined;
152
+ host_rss_mib?: number | undefined;
153
+ gpu_mib?: number | undefined;
154
+ }, {
155
+ model: string;
156
+ config: string;
157
+ since: string;
158
+ device: string;
159
+ state: "hot" | "warming" | "paused" | "draining";
160
+ endpoint?: string | undefined;
161
+ engine?: string | undefined;
162
+ unit?: string | undefined;
163
+ host_rss_mib?: number | undefined;
164
+ gpu_mib?: number | undefined;
165
+ holders?: string[] | undefined;
166
+ }>, "many">;
167
+ pending: z.ZodArray<z.ZodObject<{
168
+ id: z.ZodString;
169
+ requested_model: z.ZodString;
170
+ client: z.ZodOptional<z.ZodString>;
171
+ session: z.ZodOptional<z.ZodString>;
172
+ pid: z.ZodOptional<z.ZodNumber>;
173
+ created_at: z.ZodString;
174
+ expires_at: z.ZodString;
175
+ reason: z.ZodOptional<z.ZodString>;
176
+ /** The request's context need in cells (tokens + generation headroom),
177
+ * as estimated by the proxy when it parked the request. Option math is
178
+ * per-request (spec §4.4): cost(config, cells) — without this, the
179
+ * operator's `apply` and the pending list would recompute options
180
+ * against the 32k default instead of what the parked client needs.
181
+ * Absent on entries parked by older proxies → the default applies. */
182
+ cells: z.ZodOptional<z.ZodNumber>;
183
+ }, "strip", z.ZodTypeAny, {
184
+ id: string;
185
+ created_at: string;
186
+ requested_model: string;
187
+ expires_at: string;
188
+ reason?: string | undefined;
189
+ client?: string | undefined;
190
+ session?: string | undefined;
191
+ pid?: number | undefined;
192
+ cells?: number | undefined;
193
+ }, {
194
+ id: string;
195
+ created_at: string;
196
+ requested_model: string;
197
+ expires_at: string;
198
+ reason?: string | undefined;
199
+ client?: string | undefined;
200
+ session?: string | undefined;
201
+ pid?: number | undefined;
202
+ cells?: number | undefined;
203
+ }>, "many">;
204
+ /** Last-served clock for the idle sweep (§4.4.2): model key → epoch ms
205
+ * of the most recent gated request forwarded to a live resident of
206
+ * that model. Survives syncLedger's wholesale resident rebuild by
207
+ * living at the top level; entries for models with no live resident
208
+ * are harmless residue. Absent on pre-§4.4.2 ledgers → the default. */
209
+ usage: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodNumber>>;
210
+ }, "strip", z.ZodTypeAny, {
211
+ version: 1;
212
+ pending: {
213
+ id: string;
214
+ created_at: string;
215
+ requested_model: string;
216
+ expires_at: string;
217
+ reason?: string | undefined;
218
+ client?: string | undefined;
219
+ session?: string | undefined;
220
+ pid?: number | undefined;
221
+ cells?: number | undefined;
222
+ }[];
223
+ usage: Record<string, number>;
224
+ devices: Record<string, {
225
+ kind: "cpu" | "gpu";
226
+ source?: string | undefined;
227
+ total_mib?: number | undefined;
228
+ reserved_mib?: number | undefined;
229
+ free_mib?: number | undefined;
230
+ }>;
231
+ residents: {
232
+ model: string;
233
+ config: string;
234
+ since: string;
235
+ device: string;
236
+ state: "hot" | "warming" | "paused" | "draining";
237
+ holders: string[];
238
+ endpoint?: string | undefined;
239
+ engine?: string | undefined;
240
+ unit?: string | undefined;
241
+ host_rss_mib?: number | undefined;
242
+ gpu_mib?: number | undefined;
243
+ }[];
244
+ updated_at?: string | undefined;
245
+ }, {
246
+ version: 1;
247
+ pending: {
248
+ id: string;
249
+ created_at: string;
250
+ requested_model: string;
251
+ expires_at: string;
252
+ reason?: string | undefined;
253
+ client?: string | undefined;
254
+ session?: string | undefined;
255
+ pid?: number | undefined;
256
+ cells?: number | undefined;
257
+ }[];
258
+ devices: Record<string, {
259
+ kind: "cpu" | "gpu";
260
+ source?: string | undefined;
261
+ total_mib?: number | undefined;
262
+ reserved_mib?: number | undefined;
263
+ free_mib?: number | undefined;
264
+ }>;
265
+ residents: {
266
+ model: string;
267
+ config: string;
268
+ since: string;
269
+ device: string;
270
+ state: "hot" | "warming" | "paused" | "draining";
271
+ endpoint?: string | undefined;
272
+ engine?: string | undefined;
273
+ unit?: string | undefined;
274
+ host_rss_mib?: number | undefined;
275
+ gpu_mib?: number | undefined;
276
+ holders?: string[] | undefined;
277
+ }[];
278
+ usage?: Record<string, number> | undefined;
279
+ updated_at?: string | undefined;
280
+ }>;
281
+ export type PlacementLedger = z.infer<typeof PlacementLedgerSchema>;
282
+ export declare function placementLedgerPath(): string;
283
+ export declare function emptyLedger(): PlacementLedger;
284
+ export declare function loadLedger(path?: string): PlacementLedger;
285
+ export declare function acquireLedgerLock(path: string, timeoutMs?: number): boolean;
286
+ export declare function releaseLedgerLock(path: string): void;
287
+ /** Mutate the ledger under the advisory lock: re-read, apply, stamp
288
+ * updated_at, write atomically. Throws (fails closed) when the lock cannot
289
+ * be taken — two writers must never interleave. `lockTimeoutMs` bounds the
290
+ * wait (default 5000ms); hot-path callers that only ADVANCE a clock use a
291
+ * short fuse so contention costs them a lost update, never a stalled
292
+ * request (see the admit forward touch, placement-routes.ts). */
293
+ export declare function withLedger(path: string | undefined, mutate: (ledger: PlacementLedger) => void, opts?: {
294
+ lockTimeoutMs?: number;
295
+ }): PlacementLedger;
296
+ /** Live free memory for every registry device, from the probes. gpuN keys map
297
+ * to nvidia-smi index N; cpu keys read MemAvailable. Unprobeable devices
298
+ * carry no free_mib — unknown, never guessed. */
299
+ export declare function probeDeviceStates(registry: ModelRegistry, opts?: {
300
+ gpu?: Map<number, {
301
+ total_mib?: number;
302
+ free_mib?: number;
303
+ }>;
304
+ hostAvailable?: number | null;
305
+ }): Record<string, LedgerDeviceState>;
306
+ /** Derive residents from live unit activity. A registry config whose unit
307
+ * is active IS a resident (fact), regardless of measurement — the ledger
308
+ * records what runs; measurement gates what may be LOADED. Measured
309
+ * residents carry their declared cost so the admission budget sum has real
310
+ * numbers; unmeasured ones carry none and admission fails closed on them.
311
+ * The `active` predicate is injectable for tests. */
312
+ export declare function deriveLiveResidents(registry: ModelRegistry, isActive?: (unit: string) => boolean): LedgerResident[];
313
+ /** Refresh device facts + reconcile residents in one locked write. Returns
314
+ * the resulting ledger. Measured gpu/host figures on reconciled residents
315
+ * come from the registry measurement when present (display), from the live
316
+ * process when possible — Phase 1 keeps registry values and leaves live
317
+ * attribution to `measure`. */
318
+ export declare function syncLedger(registry: ModelRegistry, path?: string, isActive?: (unit: string) => boolean, opts?: {
319
+ gpuFree?: Map<number, number>;
320
+ hostAvailable?: number | null;
321
+ deviceProbe?: typeof probeDeviceStates;
322
+ }): PlacementLedger;
323
+ //# sourceMappingURL=ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.d.ts","sourceRoot":"","sources":["../../src/placement/ledger.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAErE,eAAO,MAAM,cAAc,IAAI,CAAC;AAMhC,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;EAMlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,mBAAmB,qDAAmD,CAAC;AACpF,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAY/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,mBAAmB;;;;;;;;;IAS9B;;;;;0EAKsE;;;;;;;;;;;;;;;;;;;;;;EAEtE,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;4EAI4E;AAC5E,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,MAAM,CAEjE;AAED,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAnBhC;;;;;8EAKsE;;;;;;;;;;;;;;;;;;;;;;;IAoBtE;;;;2EAIuE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAEvE,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAMpE,wBAAgB,mBAAmB,IAAI,MAAM,CAE5C;AAED,wBAAgB,WAAW,IAAI,eAAe,CAE7C;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,MAA8B,GAAG,eAAe,CAQhF;AAiBD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,SAAO,GAAG,OAAO,CAsBzE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED;;;;;iEAKiE;AACjE,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,YAAwB,EACpC,MAAM,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,IAAI,EACzC,IAAI,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACpC,eAAe,CAajB;AAMD;;iDAEiD;AACjD,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,EACvB,IAAI,CAAC,EAAE;IAAE,GAAG,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CA0BnC;AAMD;;;;;qDAKqD;AACrD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EACvB,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAqB,GAChD,cAAc,EAAE,CAsBlB;AAED;;;;+BAI+B;AAC/B,wBAAgB,UAAU,CACxB,QAAQ,EAAE,aAAa,EACvB,IAAI,GAAE,MAA8B,EACpC,QAAQ,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAqB,EACjD,IAAI,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,iBAAiB,CAAA;CAAE,GAC9G,eAAe,CAyBjB"}
@@ -0,0 +1,263 @@
1
+ /**
2
+ * Placement ledger — `~/.uap/placement.json` (spec §4.2).
3
+ *
4
+ * Live state: devices (probe fact), residents (what is loaded, with state),
5
+ * pending placement requests (what is parked awaiting the operator).
6
+ *
7
+ * The ledger records intent; the probe records fact. Reads refresh device
8
+ * free memory from the live probes — never from the last writer's snapshot —
9
+ * and disagreement between the two is surfaced, not averaged.
10
+ *
11
+ * Writers hold an advisory lock (the mkdirSync-atomic pattern from
12
+ * src/cli/proxy-lifecycle.ts) and re-read before committing, so a stale view
13
+ * cannot clobber. Lock acquisition fails CLOSED (throws): a lost ledger write
14
+ * must be loud.
15
+ */
16
+ import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'fs';
17
+ import { homedir } from 'os';
18
+ import { dirname, join } from 'path';
19
+ import { z } from 'zod';
20
+ import { gpuStatsByIndex, hostAvailableMiB } from './probes.js';
21
+ import { isMeasuredConfig } from './registry.js';
22
+ export const LEDGER_VERSION = 1;
23
+ // ---------------------------------------------------------------------------
24
+ // Schema
25
+ // ---------------------------------------------------------------------------
26
+ export const LedgerDeviceStateSchema = z.object({
27
+ kind: z.enum(['gpu', 'cpu']),
28
+ total_mib: z.number().int().positive().optional(),
29
+ free_mib: z.number().int().nonnegative().optional(),
30
+ reserved_mib: z.number().int().nonnegative().optional(),
31
+ source: z.string().optional(),
32
+ });
33
+ export const ResidentStateSchema = z.enum(['hot', 'warming', 'paused', 'draining']);
34
+ export const LedgerResidentSchema = z.object({
35
+ model: z.string(),
36
+ config: z.string(),
37
+ device: z.string(),
38
+ engine: z.string().optional(),
39
+ endpoint: z.string().optional(),
40
+ unit: z.string().optional(),
41
+ state: ResidentStateSchema,
42
+ gpu_mib: z.number().int().positive().optional(),
43
+ host_rss_mib: z.number().int().positive().optional(),
44
+ holders: z.array(z.string()).default([]),
45
+ since: z.string(),
46
+ });
47
+ export const LedgerPendingSchema = z.object({
48
+ id: z.string(),
49
+ requested_model: z.string(),
50
+ client: z.string().optional(),
51
+ session: z.string().optional(),
52
+ pid: z.number().int().positive().optional(),
53
+ created_at: z.string(),
54
+ expires_at: z.string(),
55
+ reason: z.string().optional(),
56
+ /** The request's context need in cells (tokens + generation headroom),
57
+ * as estimated by the proxy when it parked the request. Option math is
58
+ * per-request (spec §4.4): cost(config, cells) — without this, the
59
+ * operator's `apply` and the pending list would recompute options
60
+ * against the 32k default instead of what the parked client needs.
61
+ * Absent on entries parked by older proxies → the default applies. */
62
+ cells: z.number().int().positive().optional(),
63
+ });
64
+ /** Canonical placement TARGET identity for a resident: `device:endpoint`
65
+ * (spec §5.1 — the placement identity, never the model name). One composer,
66
+ * one spelling: the dashboard's admit answer, the proxy's per-target
67
+ * semaphore budget, and (phase 3) coordination leases must key off the
68
+ * identical string, so it is exported rather than re-derived per caller. */
69
+ export function residentTargetId(resident) {
70
+ return `${resident.device}:${resident.endpoint ?? 'default'}`;
71
+ }
72
+ export const PlacementLedgerSchema = z.object({
73
+ version: z.literal(LEDGER_VERSION),
74
+ updated_at: z.string().optional(),
75
+ devices: z.record(z.string(), LedgerDeviceStateSchema),
76
+ residents: z.array(LedgerResidentSchema),
77
+ pending: z.array(LedgerPendingSchema),
78
+ /** Last-served clock for the idle sweep (§4.4.2): model key → epoch ms
79
+ * of the most recent gated request forwarded to a live resident of
80
+ * that model. Survives syncLedger's wholesale resident rebuild by
81
+ * living at the top level; entries for models with no live resident
82
+ * are harmless residue. Absent on pre-§4.4.2 ledgers → the default. */
83
+ usage: z.record(z.string(), z.number()).default({}),
84
+ });
85
+ // ---------------------------------------------------------------------------
86
+ // Paths, load, save
87
+ // ---------------------------------------------------------------------------
88
+ export function placementLedgerPath() {
89
+ return process.env.UAP_PLACEMENT_LEDGER ?? join(homedir(), '.uap', 'placement.json');
90
+ }
91
+ export function emptyLedger() {
92
+ return { version: LEDGER_VERSION, devices: {}, residents: [], pending: [], usage: {} };
93
+ }
94
+ export function loadLedger(path = placementLedgerPath()) {
95
+ if (!existsSync(path))
96
+ return emptyLedger();
97
+ try {
98
+ const parsed = PlacementLedgerSchema.safeParse(JSON.parse(readFileSync(path, 'utf-8')));
99
+ return parsed.success ? parsed.data : emptyLedger(); // corrupt → empty, fail closed
100
+ }
101
+ catch {
102
+ return emptyLedger(); // unparseable → empty, never a partial read
103
+ }
104
+ }
105
+ /** Atomic write: tmp file + rename, so a reader never sees a torn document. */
106
+ function saveLedgerAtomically(path, ledger) {
107
+ mkdirSync(dirname(path), { recursive: true });
108
+ const tmp = `${path}.tmp-${process.pid}`;
109
+ writeFileSync(tmp, `${JSON.stringify(ledger, null, 2)}\n`);
110
+ renameSync(tmp, path);
111
+ }
112
+ // ---------------------------------------------------------------------------
113
+ // Advisory lock (mkdirSync-atomic, stale-steal — proxy-lifecycle pattern,
114
+ // but fail-CLOSED: a ledger writer that cannot take the lock must not write)
115
+ // ---------------------------------------------------------------------------
116
+ const LOCK_STALE_MS = 30_000;
117
+ export function acquireLedgerLock(path, timeoutMs = 5000) {
118
+ const lock = `${path}.lock`;
119
+ mkdirSync(dirname(lock), { recursive: true });
120
+ const deadline = Date.now() + timeoutMs;
121
+ for (;;) {
122
+ try {
123
+ mkdirSync(lock); // atomic: succeeds only if it did not exist
124
+ return true;
125
+ }
126
+ catch {
127
+ try {
128
+ const st = statSync(lock);
129
+ if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
130
+ rmSync(lock, { recursive: true, force: true }); // steal a crashed holder
131
+ continue;
132
+ }
133
+ }
134
+ catch {
135
+ continue; // vanished between attempts — retry
136
+ }
137
+ if (Date.now() >= deadline)
138
+ return false;
139
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); // sleep 100ms, sync context
140
+ }
141
+ }
142
+ }
143
+ export function releaseLedgerLock(path) {
144
+ rmSync(`${path}.lock`, { recursive: true, force: true });
145
+ }
146
+ /** Mutate the ledger under the advisory lock: re-read, apply, stamp
147
+ * updated_at, write atomically. Throws (fails closed) when the lock cannot
148
+ * be taken — two writers must never interleave. `lockTimeoutMs` bounds the
149
+ * wait (default 5000ms); hot-path callers that only ADVANCE a clock use a
150
+ * short fuse so contention costs them a lost update, never a stalled
151
+ * request (see the admit forward touch, placement-routes.ts). */
152
+ export function withLedger(path = placementLedgerPath(), mutate, opts = {}) {
153
+ if (!acquireLedgerLock(path, opts.lockTimeoutMs)) {
154
+ throw new Error(`placement ledger lock held longer than expected: ${path}.lock`);
155
+ }
156
+ try {
157
+ const ledger = loadLedger(path);
158
+ mutate(ledger);
159
+ ledger.updated_at = new Date().toISOString();
160
+ saveLedgerAtomically(path, ledger);
161
+ return ledger;
162
+ }
163
+ finally {
164
+ releaseLedgerLock(path);
165
+ }
166
+ }
167
+ // ---------------------------------------------------------------------------
168
+ // Device fact: probe live, overlay onto registry shape
169
+ // ---------------------------------------------------------------------------
170
+ /** Live free memory for every registry device, from the probes. gpuN keys map
171
+ * to nvidia-smi index N; cpu keys read MemAvailable. Unprobeable devices
172
+ * carry no free_mib — unknown, never guessed. */
173
+ export function probeDeviceStates(registry, opts) {
174
+ const gpu = opts?.gpu ?? gpuStatsByIndex();
175
+ const hostAvailable = opts?.hostAvailable !== undefined ? opts.hostAvailable : hostAvailableMiB();
176
+ const out = {};
177
+ for (const [key, dev] of Object.entries(registry.devices)) {
178
+ const state = { kind: dev.kind, total_mib: dev.total_mib, reserved_mib: dev.reserved_mib };
179
+ if (dev.kind === 'gpu') {
180
+ const idx = /^gpu(\d+)$/.exec(key);
181
+ const stats = idx ? gpu.get(Number(idx[1])) : undefined;
182
+ if (stats?.free_mib !== undefined) {
183
+ state.free_mib = stats.free_mib;
184
+ state.source = 'nvidia-smi';
185
+ }
186
+ else {
187
+ state.source = 'unprobed';
188
+ }
189
+ }
190
+ else {
191
+ if (typeof hostAvailable === 'number') {
192
+ state.free_mib = hostAvailable;
193
+ state.source = 'MemAvailable';
194
+ }
195
+ else {
196
+ state.source = 'unprobed';
197
+ }
198
+ }
199
+ out[key] = state;
200
+ }
201
+ return out;
202
+ }
203
+ // ---------------------------------------------------------------------------
204
+ // Reconcile: align ledger residents with what is actually running
205
+ // ---------------------------------------------------------------------------
206
+ /** Derive residents from live unit activity. A registry config whose unit
207
+ * is active IS a resident (fact), regardless of measurement — the ledger
208
+ * records what runs; measurement gates what may be LOADED. Measured
209
+ * residents carry their declared cost so the admission budget sum has real
210
+ * numbers; unmeasured ones carry none and admission fails closed on them.
211
+ * The `active` predicate is injectable for tests. */
212
+ export function deriveLiveResidents(registry, isActive = () => false) {
213
+ const residents = [];
214
+ for (const [mKey, model] of Object.entries(registry.models)) {
215
+ for (const [cKey, cfg] of Object.entries(model.configs)) {
216
+ const unit = cfg.unit ?? model.unit;
217
+ if (!unit || !isActive(unit))
218
+ continue;
219
+ residents.push({
220
+ model: mKey,
221
+ config: cKey,
222
+ device: model.affinity?.device[0] ?? 'gpu0',
223
+ engine: cfg.engine ?? model.engine,
224
+ endpoint: model.endpoint,
225
+ unit,
226
+ state: 'hot',
227
+ gpu_mib: isMeasuredConfig(cfg) ? cfg.resident_gpu_mib : undefined,
228
+ host_rss_mib: isMeasuredConfig(cfg) ? cfg.host_rss_mib : undefined,
229
+ holders: [],
230
+ since: new Date().toISOString(),
231
+ });
232
+ }
233
+ }
234
+ return residents;
235
+ }
236
+ /** Refresh device facts + reconcile residents in one locked write. Returns
237
+ * the resulting ledger. Measured gpu/host figures on reconciled residents
238
+ * come from the registry measurement when present (display), from the live
239
+ * process when possible — Phase 1 keeps registry values and leaves live
240
+ * attribution to `measure`. */
241
+ export function syncLedger(registry, path = placementLedgerPath(), isActive = () => false, opts) {
242
+ const devices = opts?.deviceProbe
243
+ ? opts.deviceProbe(registry, opts)
244
+ : probeDeviceStates(registry, opts);
245
+ const residents = deriveLiveResidents(registry, isActive);
246
+ return withLedger(path, (ledger) => {
247
+ ledger.devices = devices;
248
+ // `draining` markers survive the wholesale resident rebuild: an
249
+ // enforcement's drain window can outlast a concurrent `models status`
250
+ // or dashboard auto-refresh, and resetting victims to hot would
251
+ // re-open admission to a unit being stopped (spec §4.6 step 1's
252
+ // "refuse new requests" must hold for the whole sequence).
253
+ const draining = new Set(ledger.residents
254
+ .filter((r) => r.state === 'draining')
255
+ .map((r) => `${r.model}/${r.config}`));
256
+ ledger.residents = residents.map((r) => draining.has(`${r.model}/${r.config}`) ? { ...r, state: 'draining' } : r);
257
+ // Pending entries survive the sync: they are requests awaiting the
258
+ // operator, not backend state. Expired ones are pruned.
259
+ const now = new Date().toISOString();
260
+ ledger.pending = ledger.pending.filter((p) => p.expires_at > now);
261
+ });
262
+ }
263
+ //# sourceMappingURL=ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ledger.js","sourceRoot":"","sources":["../../src/placement/ledger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Impact preview (spec §4.5): the victim list the operator sees BEFORE
3
+ * committing, and the re-derivation check enforcement uses AFTER the yes.
4
+ *
5
+ * Nothing in this view is advisory-only: enforcement re-derives the same
6
+ * victim set and refuses to proceed if it differs from what the operator
7
+ * approved. A preview that lies is worse than no preview.
8
+ *
9
+ * In-flight status is `unknown` in phase 1: the proxy exposes no live
10
+ * request state yet (the phase-2 in-flight-per-target endpoint supplies it).
11
+ * The preview must say unknown rather than guess "idle".
12
+ */
13
+ import { type ModelRegistry } from './registry.js';
14
+ import type { LedgerResident } from './ledger.js';
15
+ export type InFlight = 'unknown' | boolean;
16
+ export interface VictimPreview {
17
+ model: string;
18
+ config: string;
19
+ device: string;
20
+ state: LedgerResident['state'];
21
+ holders: string[];
22
+ inflight: InFlight;
23
+ /** MiB the eviction returns to the device; unknown → undefined. */
24
+ frees_gpu_mib: number;
25
+ /** MiB the eviction returns to host RAM; unknown → undefined. */
26
+ frees_host_mib?: number;
27
+ /** Measured cost of loading this config back, if the registry knows it. */
28
+ reload_cost_mib?: number;
29
+ /** Human consequence from the spec §4.5 table. */
30
+ consequence: string;
31
+ }
32
+ /** Build the victim preview for a displace option. */
33
+ export declare function buildVictimPreviews(registry: ModelRegistry, victims: LedgerResident[], inflightOf?: (victim: LedgerResident) => InFlight): VictimPreview[];
34
+ /** Stable signature of a victim set — what the operator approved and what
35
+ * enforcement re-derives must produce the same string, or enforcement
36
+ * aborts. Order-insensitive; content-sensitive. */
37
+ export declare function victimSignature(victims: Array<{
38
+ model: string;
39
+ config: string;
40
+ }>): string;
41
+ /** The enforcement-time check: re-derive the victims from live state and
42
+ * refuse when the set the operator approved no longer matches. A mismatch
43
+ * means the world moved between preview and commit — abort, re-ask. */
44
+ export declare function previewStillValid(approved: Array<{
45
+ model: string;
46
+ config: string;
47
+ }>, rederived: Array<{
48
+ model: string;
49
+ config: string;
50
+ }>): boolean;
51
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/placement/preview.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAE3C,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,QAAQ,CAAC;IACnB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2EAA2E;IAC3E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kDAAkD;IAClD,WAAW,EAAE,MAAM,CAAC;CACrB;AAaD,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,cAAc,EAAE,EACzB,UAAU,GAAE,CAAC,MAAM,EAAE,cAAc,KAAK,QAA0B,GACjE,aAAa,EAAE,CAgBjB;AAED;;mDAEmD;AACnD,wBAAgB,eAAe,CAAC,OAAO,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,MAAM,CAKzF;AAED;;uEAEuE;AACvE,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,EAClD,SAAS,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,GAClD,OAAO,CAET"}