@miller-tech/uap 2.21.2 → 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,562 @@
1
+ /**
2
+ * Per-session sandbox grants (the D2 consent ledger).
3
+ *
4
+ * `uap sandbox` is all-or-nothing authority: the workdir + scratch are
5
+ * writable, everything else is EROFS. That default is right, but real work
6
+ * sometimes needs one more hole (a config dir, a build cache outside the
7
+ * tree) — and the legacy answer, `UAP_SANDBOX_ALLOW=path:path`, was
8
+ * UNRECORDED authority: an env var nobody audited, invisible to the
9
+ * operator, surviving in shell histories and hook templates forever.
10
+ *
11
+ * This ledger is the fix: every extra hole is a GRANT — bounded by a
12
+ * session, a TTL, and a stated reason, recorded with who consented, and
13
+ * audit-logged on use. "Every new authority must state who consented"
14
+ * (ADR 0011, the P4 bar).
15
+ *
16
+ * Design rules (inherited from the attention-queue store, review-hardened):
17
+ * - the ledger is shared-write state in `.uap/`, so every string is
18
+ * sanitized at INGEST and the load path re-applies the same sanitizer —
19
+ * a planted row must not become a kernel-writable hole or reach
20
+ * operator terminals;
21
+ * - atomic writes with unique tmp names (no fixed `.tmp` in shared dirs);
22
+ * - corrupt/unreadable ledger ⇒ EMPTY ledger: the safe direction for an
23
+ * authority store is least authority (the sandbox itself stays the real
24
+ * kernel boundary — a lost grant just means EROFS, not a breach);
25
+ * - every grant expires (default 8h, hard cap 7d): authority is bounded
26
+ * by construction, and revocation is a single command;
27
+ * - fail-OPEN for the sandbox, fail-CLOSED for the holes: load problems
28
+ * never crash a sandboxed run, they just yield no holes.
29
+ */
30
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'fs';
31
+ import { randomBytes, randomUUID } from 'crypto';
32
+ import { homedir } from 'os';
33
+ import { dirname, join } from 'path';
34
+ import { mainCheckoutRoot } from '../utils/checkout-root.js';
35
+ import { ledgerHeadOf, pathCoversAnchorDir, readAnchor, verifyLedger, writeAnchor } from './sandbox-anchor.js';
36
+ /** Audit-log retention: the trail is append-only history, but an unbounded
37
+ * JSONL under the shared-write `.uap/` grows forever — past
38
+ * the trigger it is compacted to the newest AUDIT_MAX_LINES event lines
39
+ * (plus the triggering event and one `audit-compaction` marker), so the
40
+ * file lands at AUDIT_MAX_LINES + 2. The compaction is itself audited, so
41
+ * a pruned trail is an EXPECTED state, never a silent one. */
42
+ export const AUDIT_MAX_LINES = 10_000;
43
+ export const AUDIT_PRUNE_TRIGGER = 12_000;
44
+ /** Default grant TTL: 8h — long enough for a working session, short enough
45
+ * that a forgotten hole closes itself before the next one. */
46
+ export const DEFAULT_GRANT_TTL_SECS = 28_800;
47
+ /** Hard cap on grant TTL: authority must be re-consented weekly at most. */
48
+ export const MAX_GRANT_TTL_SECS = 604_800;
49
+ /** Paths per grant cap: a grant is a scoped exception, not a hole list. */
50
+ export const MAX_PATHS_PER_GRANT = 8;
51
+ /**
52
+ * Ledger file: `<mainCheckoutRoot>/.uap/sandbox-grants.json` — ONE ledger
53
+ * per checkout, not per cwd, anchored at the main checkout root like the
54
+ * attention queue (ADR 0010): the CLI can be invoked from any worktree
55
+ * subdir, and a per-cwd ledger would give the same session different
56
+ * authority depending on where the operator happened to stand. Today the
57
+ * CLI is the only consumer; the anchoring also keeps a future dashboard
58
+ * grants view (a natural P5 surface) reading the same file.
59
+ * `UAP_SANDBOX_GRANTS` pins an explicit file (tests, odd layouts).
60
+ */
61
+ export function sandboxGrantsPath(projectRoot) {
62
+ const override = process.env.UAP_SANDBOX_GRANTS;
63
+ if (typeof override === 'string' && override.trim() !== '')
64
+ return override.trim();
65
+ return join(mainCheckoutRoot(projectRoot), '.uap', 'sandbox-grants.json');
66
+ }
67
+ /** Audit trail: append-only JSONL, one line per grant/revoke/sandbox-use. */
68
+ export function sandboxAuditPath(projectRoot) {
69
+ const override = process.env.UAP_SANDBOX_AUDIT;
70
+ if (typeof override === 'string' && override.trim() !== '')
71
+ return override.trim();
72
+ return join(mainCheckoutRoot(projectRoot), '.uap', 'sandbox-audit.jsonl');
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // Sanitization — the ledger grants KERNEL-WRITABLE HOLES and feeds operator
76
+ // terminals; ingested strings are untrusted by construction.
77
+ // ---------------------------------------------------------------------------
78
+ // eslint-disable-next-line no-control-regex -- stripping control characters IS the point
79
+ const CONTROL_CHARS = /[\x00-\x1f\x7f]/g;
80
+ function clean(s, max) {
81
+ if (typeof s !== 'string')
82
+ return '';
83
+ return s.replace(CONTROL_CHARS, ' ').trim().slice(0, max);
84
+ }
85
+ /**
86
+ * A grantable path must be ABSOLUTE, must exist (you grant a real hole, not
87
+ * a speculative one), and must not be an over-broad root — checked AFTER
88
+ * symlink resolution: `/tmp/link -> $HOME` passes a string-only refusal and
89
+ * bwrap's `--bind` follows the link, so the check that matters is against
90
+ * the REALPATH (`/`, `/home`, $HOME itself — the same refusal the sandbox
91
+ * applies to a too-broad workdir: an over-broad grant IS a sandbox escape).
92
+ * Returns the RESOLVED path (what the bind will actually target), or null.
93
+ */
94
+ export function validateGrantPath(p) {
95
+ if (!p.startsWith('/'))
96
+ return null;
97
+ const home = process.env.HOME || homedir();
98
+ let resolved;
99
+ try {
100
+ if (!existsSync(p))
101
+ return null;
102
+ resolved = realpathSync(p);
103
+ }
104
+ catch {
105
+ return null;
106
+ }
107
+ if (resolved === '/' || resolved === '/home' || resolved === home)
108
+ return null;
109
+ return resolved;
110
+ }
111
+ function sanitizeGrant(raw) {
112
+ // `id` goes through the SAME clean() as every other string field: the row is
113
+ // rendered on operator terminals (the grants listing, the dashboard section,
114
+ // the revoke command line), and a planted id carrying ANSI/OSC bytes would
115
+ // fire exactly when the operator is drawn to inspect a tamper alarm
116
+ // (review M1, 2026-10-11). trim() alone was the one gap.
117
+ const id = clean(raw.id, 64);
118
+ const sessionId = clean(raw.sessionId, 64);
119
+ const reason = clean(raw.reason, 300);
120
+ const grantedBy = clean(raw.grantedBy, 64);
121
+ if (!id ||
122
+ !sessionId ||
123
+ !reason || // a grant without a stated reason is exactly what this ledger exists to refuse
124
+ !grantedBy ||
125
+ typeof raw.grantedAt !== 'string' ||
126
+ typeof raw.expiresAt !== 'string') {
127
+ return null;
128
+ }
129
+ const paths = [];
130
+ const rawPaths = Array.isArray(raw.paths) ? raw.paths : [];
131
+ for (const rp of rawPaths) {
132
+ if (typeof rp !== 'string')
133
+ return null;
134
+ // The load path re-applies the FULL grantable-path validation, not just
135
+ // a string prefix: a planted well-formed row with `paths:["/"]` would
136
+ // otherwise become a real `--bind / /` hole (the bind follows the row,
137
+ // so the row must carry nothing the operator would have refused — the
138
+ // review's blocker finding). A row with ANY
139
+ // ungrantable path is dropped whole: least authority, and a half-row
140
+ // is a forged consent record.
141
+ const p = validateGrantPath(clean(rp, 512));
142
+ if (!p)
143
+ return null;
144
+ if (!paths.includes(p))
145
+ paths.push(p);
146
+ }
147
+ if (paths.length === 0)
148
+ return null; // a grant must grant something
149
+ // Timestamps must parse, and the expiry must respect the hard TTL bound —
150
+ // a planted `expiresAt: "9999"` never lapses under the lexicographic
151
+ // compare in listAuthority, which is exactly the "forever grant" this
152
+ // ledger exists to prevent. `grantedAt` is kept as written (history), but
153
+ // must be a parseable instant.
154
+ if (!Number.isFinite(Date.parse(raw.grantedAt)))
155
+ return null;
156
+ const expiresMs = Date.parse(raw.expiresAt);
157
+ if (!Number.isFinite(expiresMs) || expiresMs > Date.now() + MAX_GRANT_TTL_SECS * 1000)
158
+ return null;
159
+ return {
160
+ id,
161
+ sessionId,
162
+ paths,
163
+ reason,
164
+ grantedBy,
165
+ grantedAt: raw.grantedAt,
166
+ expiresAt: raw.expiresAt,
167
+ };
168
+ }
169
+ // ---------------------------------------------------------------------------
170
+ // Store — least-authority load, atomic persist.
171
+ // ---------------------------------------------------------------------------
172
+ function loadGrants(projectRoot) {
173
+ try {
174
+ const path = sandboxGrantsPath(projectRoot);
175
+ if (!existsSync(path))
176
+ return [];
177
+ const parsed = JSON.parse(readFileSync(path, 'utf-8'));
178
+ if (!Array.isArray(parsed))
179
+ return [];
180
+ const grants = [];
181
+ for (const raw of parsed) {
182
+ if (typeof raw !== 'object' || raw === null)
183
+ continue;
184
+ const g = sanitizeGrant(raw);
185
+ if (g)
186
+ grants.push(g); // malformed rows are dropped, never fatal
187
+ }
188
+ return grants;
189
+ }
190
+ catch (err) {
191
+ // Unreadable ledger = empty ledger: the SAFE direction for authority.
192
+ // The sandbox keeps its kernel boundary; only the extra holes vanish.
193
+ console.warn(`[sandbox-grants] ledger unreadable, treating as empty (no extra holes): ${err instanceof Error ? err.message : String(err)}`);
194
+ return [];
195
+ }
196
+ }
197
+ function persistGrants(projectRoot, grants) {
198
+ try {
199
+ const path = sandboxGrantsPath(projectRoot);
200
+ // Create the TARGET's directory (not <cwd>/.uap): the ledger may live at
201
+ // the main checkout root while cwd is a worktree subdir, or at a
202
+ // UAP_SANDBOX_GRANTS override — mkdir-ing the wrong place fails the
203
+ // write it was supposed to enable.
204
+ mkdirSync(dirname(path), { recursive: true });
205
+ // Unique tmp name: a fixed `.tmp` in shared-write .uap/ is an
206
+ // interleave-truncate and symlink-clobber target (review finding).
207
+ const tmp = `${path}.${process.pid}-${randomBytes(3).toString('hex')}.tmp`;
208
+ writeFileSync(tmp, JSON.stringify(grants, null, 2), 'utf-8');
209
+ renameSync(tmp, path);
210
+ return true;
211
+ }
212
+ catch (err) {
213
+ // A failed persist is reported, never silent — but it must not throw:
214
+ // the caller (sandbox launch) treats "no persisted grants" as safe.
215
+ console.warn(`[sandbox-grants] ledger persist failed (grants NOT active for this session): ${err instanceof Error ? err.message : String(err)}`);
216
+ return false;
217
+ }
218
+ }
219
+ /** The last parseable seq in an audit line list (0 when none parse): seqs
220
+ * are monotonic, so the last parseable line IS the high-water. A malformed
221
+ * tail degrades to the last good seq, never to a collision. */
222
+ function scanHighWater(lines) {
223
+ for (let i = lines.length - 1; i >= 0; i--) {
224
+ try {
225
+ const last = JSON.parse(lines[i]);
226
+ if (typeof last.seq === 'number' && Number.isFinite(last.seq))
227
+ return last.seq;
228
+ }
229
+ catch {
230
+ /* keep scanning */
231
+ }
232
+ }
233
+ return 0;
234
+ }
235
+ /** The audit file's high-water seq (0 when the file is absent, empty, or
236
+ * wholly malformed) — what anchorMutation witnesses after an operation's
237
+ * line has landed. */
238
+ function auditHighWater(projectRoot) {
239
+ try {
240
+ const path = sandboxAuditPath(projectRoot);
241
+ if (!existsSync(path))
242
+ return 0;
243
+ const text = readFileSync(path, 'utf-8');
244
+ if (text.length === 0)
245
+ return 0;
246
+ return scanHighWater(text.split('\n').filter((l) => l.length > 0));
247
+ }
248
+ catch {
249
+ return 0;
250
+ }
251
+ }
252
+ /** Append one audit line. Append-only, best-effort, never throws. Every line
253
+ * carries a monotonic `seq` — recovered as the max of the file's last
254
+ * parseable seq and the ANCHOR's witnessed high-water, so numbering
255
+ * survives compaction AND file deletion (a trail whose numbering can
256
+ * restart is a forgeable trail). The file is compacted past
257
+ * AUDIT_PRUNE_TRIGGER with the drop recorded in an `audit-compaction`
258
+ * marker. */
259
+ function audit(projectRoot, event) {
260
+ try {
261
+ const path = sandboxAuditPath(projectRoot);
262
+ mkdirSync(dirname(path), { recursive: true });
263
+ let lines = [];
264
+ let fileHi = 0;
265
+ if (existsSync(path)) {
266
+ const text = readFileSync(path, 'utf-8');
267
+ if (text.length > 0) {
268
+ lines = text.split('\n').filter((l) => l.length > 0);
269
+ // Scan BACKWARDS for the last parseable line: after a compaction the
270
+ // file holds 10k lines with seqs up in the 12k+ range, so the old
271
+ // `lines.length` fallback could REUSE a live seq number. A malformed
272
+ // tail degrades to (last good seq + 1), never to a collision.
273
+ fileHi = scanHighWater(lines);
274
+ }
275
+ }
276
+ // The anchor's seqHi covers what the FILE cannot: deletion. The anchor
277
+ // only witnesses seqs at recorded mutations, so a file grown past the
278
+ // anchor (e.g. by sandbox-run lines) still numbers correctly — max of
279
+ // both.
280
+ const seq = Math.max(fileHi, readAnchor(projectRoot)?.seqHi ?? 0) + 1;
281
+ if (lines.length + 1 > AUDIT_PRUNE_TRIGGER) {
282
+ const kept = lines.slice(-AUDIT_MAX_LINES);
283
+ kept.push(JSON.stringify({ ...event, seq }));
284
+ kept.push(JSON.stringify({
285
+ ts: new Date().toISOString(),
286
+ action: 'audit-compaction',
287
+ dropped: lines.length + 1 - kept.length,
288
+ seq: seq + 1,
289
+ }));
290
+ const tmp = `${path}.${process.pid}-${randomBytes(3).toString('hex')}.tmp`;
291
+ writeFileSync(tmp, kept.map((l) => `${l}\n`).join(''), 'utf-8');
292
+ renameSync(tmp, path);
293
+ return;
294
+ }
295
+ appendFileSync(path, `${JSON.stringify({ ...event, seq })}\n`, 'utf-8');
296
+ }
297
+ catch (err) {
298
+ console.warn(`[sandbox-grants] audit append failed (grant/revoke still applied): ${err instanceof Error ? err.message : String(err)}`);
299
+ }
300
+ }
301
+ // ---------------------------------------------------------------------------
302
+ // Trust anchor (ADR 0011 follow-up, shipped): see sandbox-anchor.ts.
303
+ // ---------------------------------------------------------------------------
304
+ /** Verify the SANITIZED on-disk ledger against the out-of-checkout anchor.
305
+ * 'unanchored' is the pre-bootstrap state, not a failure. When `grants` is
306
+ * passed (an already-loaded sanitized snapshot), the SAME snapshot is
307
+ * verified and then acted on — the torn-read fix (a review finding):
308
+ * loading the file twice left a window where the hash saw one state and
309
+ * the resolver acted on another. */
310
+ export function verifyLedgerIntegrity(projectRoot, grants) {
311
+ const onDisk = grants ?? loadGrants(projectRoot);
312
+ return verifyLedger(projectRoot, ledgerHeadOf(onDisk));
313
+ }
314
+ /** Advance the anchor to a CONFIRMED on-disk state (called only after the
315
+ * lost-update guard proved the persist landed, and AFTER the operation's
316
+ * audit line is written, so the seq high-water the anchor witnesses
317
+ * includes it). A failed anchor write is loud but never fatal: the ledger
318
+ * stays operative; verification is simply not possible until the next
319
+ * recorded mutation succeeds. */
320
+ function anchorMutation(projectRoot, confirmed) {
321
+ const prior = readAnchor(projectRoot);
322
+ const seqHi = Math.max(prior?.seqHi ?? 0, auditHighWater(projectRoot));
323
+ if (!writeAnchor(projectRoot, ledgerHeadOf(confirmed), (prior?.mutations ?? 0) + 1, seqHi)) {
324
+ console.warn('[sandbox-grants] trust-anchor write failed — the ledger is active but NOT verifiable; ' +
325
+ 'the next successful grant/renew/revoke will try to anchor it');
326
+ return;
327
+ }
328
+ // Verify-after-write (review interleave finding): a concurrent writer's
329
+ // anchor landing between ours and the ledger read leaves a persistent
330
+ // false mismatch on a healthy ledger. Confirm ours is what the disk now
331
+ // holds; if not, say so — never a silent divergence.
332
+ const after = verifyLedgerIntegrity(projectRoot);
333
+ if (after.state !== 'ok') {
334
+ console.warn(`[sandbox-grants] the trust anchor moved to a concurrent writer's state after this mutation (${after.state}) — ` +
335
+ 'if `uap sandbox grants` reports a mismatch you did not cause, re-run the last grant/revoke');
336
+ }
337
+ }
338
+ // ---------------------------------------------------------------------------
339
+ // Operations.
340
+ // ---------------------------------------------------------------------------
341
+ /** Create a grant. Returns the grant, or a refusal reason. */
342
+ export function grantAuthority(projectRoot, input) {
343
+ const sessionId = clean(input.sessionId, 64);
344
+ if (!sessionId)
345
+ return { ok: false, error: 'a grant requires --session <id> (the session this authority is scoped to)' };
346
+ const reason = clean(input.reason, 300);
347
+ if (!reason)
348
+ return { ok: false, error: 'a grant requires --reason "..." — consent must be stated, not implied' };
349
+ const paths = [];
350
+ for (const p of input.paths) {
351
+ const v = validateGrantPath(p);
352
+ if (!v)
353
+ return { ok: false, error: `not grantable: '${p}' (must be an existing absolute path, and not / , /home, or $HOME itself)` };
354
+ // The one hole that must never open: a grant covering the trust-anchor
355
+ // dir lets the sandboxed side delete or corrupt the tamper evidence
356
+ // itself (the anchor is ro-bound by default, but a hole
357
+ // over it makes it writable).
358
+ if (pathCoversAnchorDir(v)) {
359
+ return { ok: false, error: `not grantable: '${p}' covers the sandbox trust-anchor directory — a hole there could destroy the tamper evidence itself` };
360
+ }
361
+ if (!paths.includes(v))
362
+ paths.push(v);
363
+ if (paths.length > MAX_PATHS_PER_GRANT)
364
+ return { ok: false, error: `more than ${MAX_PATHS_PER_GRANT} paths in one grant — split it` };
365
+ }
366
+ if (paths.length === 0) {
367
+ return { ok: false, error: 'a grant grants something: pass at least one writable path' };
368
+ }
369
+ // TTL is FAIL-CLOSED on garbage: undefined → the 8h default, but a
370
+ // non-numeric or negative --ttl is REFUSED outright (the security
371
+ // review caught bad input silently meaning "give 8h anyway" — the wrong
372
+ // direction for authority). 0 floors to 1s: a
373
+ // zero-second grant is a recorded consent that expires immediately.
374
+ let ttl;
375
+ if (input.ttlSecs === undefined) {
376
+ ttl = DEFAULT_GRANT_TTL_SECS;
377
+ }
378
+ else if (!Number.isFinite(input.ttlSecs) || input.ttlSecs < 0) {
379
+ return { ok: false, error: `--ttl must be a non-negative number of seconds (got '${input.ttlSecs}')` };
380
+ }
381
+ else {
382
+ ttl = Math.min(Math.max(1, Math.floor(input.ttlSecs)), MAX_GRANT_TTL_SECS);
383
+ }
384
+ const now = new Date();
385
+ const grant = {
386
+ id: `sbg-${randomUUID().slice(0, 8)}`,
387
+ sessionId,
388
+ paths,
389
+ reason,
390
+ grantedBy: process.env.USER || process.env.LOGNAME || 'unknown',
391
+ grantedAt: now.toISOString(),
392
+ expiresAt: new Date(now.getTime() + ttl * 1000).toISOString(),
393
+ };
394
+ for (let attempt = 0;; attempt++) {
395
+ // ONE load, ONE hash, one array to act on (the torn-read fix — a
396
+ // review finding): verifying a separate read left a window where the
397
+ // hash saw one state and the mint acted on another.
398
+ const loaded = loadGrants(projectRoot);
399
+ const integrity = verifyLedgerIntegrity(projectRoot, loaded);
400
+ if (integrity.state === 'mismatch') {
401
+ // A tampered ledger must not mint legitimacy: refuse to RECORD new
402
+ // consent on top of a state that failed integrity verification.
403
+ return {
404
+ ok: false,
405
+ error: `ledger failed integrity verification — ${integrity.detail}; refusing to record new consent until it is resolved`,
406
+ };
407
+ }
408
+ const existing = loaded.filter((g) => g.expiresAt > now.toISOString());
409
+ // Dedupe: an identical open grant (same session + same paths) is refreshed,
410
+ // not duplicated — a second `grant` for the same hole renews its TTL.
411
+ const twin = existing.find((g) => g.sessionId === grant.sessionId && g.paths.length === grant.paths.length && grant.paths.every((p) => g.paths.includes(p)));
412
+ if (twin) {
413
+ twin.expiresAt = grant.expiresAt;
414
+ twin.reason = grant.reason;
415
+ twin.grantedAt = grant.grantedAt;
416
+ // A failed persist must not be reported as a renewal: the OLD expiry
417
+ // stays active (the safe direction), and the operator keeps the truth.
418
+ if (!persistGrants(projectRoot, existing)) {
419
+ return { ok: false, error: 'ledger persist failed — the renewal is NOT active (the previous expiry stands)' };
420
+ }
421
+ // Lost-update guard: load→modify→persist has no lock, and the atomic
422
+ // rename prevents corruption, not lost updates — a concurrent writer
423
+ // landing after ours drops our renewal while we report success.
424
+ // Verify ours survived; retry once against the fresh state.
425
+ const confirmedAll = loadGrants(projectRoot);
426
+ const after = confirmedAll.find((g) => g.id === twin.id);
427
+ if (!after || after.expiresAt !== twin.expiresAt) {
428
+ if (attempt === 0)
429
+ continue;
430
+ return { ok: false, error: 'ledger changed under a concurrent writer — renewal not confirmed; retry the grant' };
431
+ }
432
+ // Audit FIRST, then anchor: the anchor witnesses the audit high-water,
433
+ // and it must include this operation's line (audit-then-anchor is what
434
+ // makes seq survive audit-file deletion — the anchor's seqHi is the
435
+ // floor the next line numbers from).
436
+ audit(projectRoot, { ts: grant.grantedAt, action: 'grant-renewed', grantId: twin.id, sessionId, paths, reason });
437
+ anchorMutation(projectRoot, confirmedAll);
438
+ return { ok: true, grant: twin };
439
+ }
440
+ const all = [...existing, grant];
441
+ if (!persistGrants(projectRoot, all)) {
442
+ return { ok: false, error: 'ledger persist failed — the grant is NOT active (refusing to report success)' };
443
+ }
444
+ // Lost-update guard (same shape as the renewal path): two concurrent
445
+ // grants can both read-modify-write and silently drop the loser's row
446
+ // while its CLI reports success — verify the row survived before
447
+ // claiming the grant active.
448
+ const confirmedAll = loadGrants(projectRoot);
449
+ if (!confirmedAll.some((g) => g.id === grant.id)) {
450
+ if (attempt === 0)
451
+ continue;
452
+ return { ok: false, error: 'ledger changed under a concurrent writer — the grant is NOT confirmed; retry' };
453
+ }
454
+ // Audit FIRST, then anchor — same reason as the renewal path above.
455
+ audit(projectRoot, { ts: grant.grantedAt, action: 'grant', grantId: grant.id, sessionId, paths, reason, ttlSecs: ttl });
456
+ anchorMutation(projectRoot, confirmedAll);
457
+ return { ok: true, grant };
458
+ }
459
+ }
460
+ /** List grants, optionally scoped to one session. Expired rows are inert. */
461
+ export function listAuthority(projectRoot, sessionId) {
462
+ const nowIso = new Date().toISOString();
463
+ const sid = sessionId ? clean(sessionId, 64) : '';
464
+ return loadGrants(projectRoot).filter((g) => g.expiresAt > nowIso && (!sid || g.sessionId === sid));
465
+ }
466
+ /** Revoke one grant by id. Returns true only when it was found AND removed
467
+ * from the ledger — a failed persist leaves the grant active, and reporting
468
+ * success over a live grant would be a false consent record. */
469
+ export function revokeAuthority(projectRoot, grantId) {
470
+ const id = clean(grantId, 64);
471
+ // A mismatched ledger may still LOSE authority (revoking is the safe
472
+ // direction) — but the revoke re-anchors the remaining rows as the new
473
+ // baseline, so a pre-existing mismatch is named loudly at the exact
474
+ // moment it stops being detectable.
475
+ const preIntegrity = verifyLedgerIntegrity(projectRoot);
476
+ for (let attempt = 0;; attempt++) {
477
+ const all = loadGrants(projectRoot);
478
+ const keep = all.filter((g) => g.id !== id);
479
+ if (keep.length === all.length)
480
+ return false;
481
+ if (!persistGrants(projectRoot, keep))
482
+ return false;
483
+ // Lost-update guard (security review): a concurrent writer landing
484
+ // after our persist can resurrect the row; verify the removal actually
485
+ // landed before reporting success, retrying once against fresh state.
486
+ const confirmedAll = loadGrants(projectRoot);
487
+ if (confirmedAll.some((g) => g.id === id)) {
488
+ if (attempt === 0)
489
+ continue;
490
+ return false;
491
+ }
492
+ // Audit FIRST, then anchor — same reason as the grant/renew paths.
493
+ audit(projectRoot, { ts: new Date().toISOString(), action: 'revoke', grantId: id });
494
+ anchorMutation(projectRoot, confirmedAll);
495
+ if (preIntegrity.state === 'mismatch') {
496
+ console.warn(`[sandbox-grants] NOTE: the ledger had failed integrity verification before this revoke — ${preIntegrity.detail}. ` +
497
+ 'The remaining rows are now anchored as the new baseline; inspect them with `uap sandbox grants`.');
498
+ }
499
+ return true;
500
+ }
501
+ }
502
+ /**
503
+ * Resolve the ACTIVE authority for a session — what `uap sandbox` will
504
+ * actually open. Expired or malformed rows are invisible here; the sandbox
505
+ * never has to reason about them.
506
+ */
507
+ export function resolveSessionGrants(projectRoot, sessionId) {
508
+ // ONE load: the snapshot that gets verified is the SAME snapshot that
509
+ // resolves the holes (the torn-read fix).
510
+ const loaded = loadGrants(projectRoot);
511
+ // Fail-CLOSED on tamper evidence (ADR 0011): a ledger that fails anchor
512
+ // verification opens ZERO grant-derived holes. The sandbox's own kernel
513
+ // boundary is unaffected — only the extra holes close (the safe direction
514
+ // for authority, same rule as an unreadable ledger).
515
+ const integrity = verifyLedgerIntegrity(projectRoot, loaded);
516
+ if (integrity.state === 'mismatch') {
517
+ console.error(`[sandbox-grants] INTEGRITY MISMATCH — ${integrity.detail}. ` +
518
+ 'Opening NO grant-derived writable holes for this session; the sandbox boundary itself is unaffected. ' +
519
+ 'Run `uap sandbox grants` to inspect the ledger.');
520
+ return { grants: [], paths: [] };
521
+ }
522
+ if (integrity.state === 'unanchored' && loaded.length > 0) {
523
+ // The quiet dangerous state (review finding): grants exist but the anchor is
524
+ // gone/never written, so tamper evidence is OFF. Never silent.
525
+ console.warn(`[sandbox-grants] ${loaded.length} grant(s) on disk but NO trust anchor — tamper evidence is inactive; ` +
526
+ 'the next successful grant/renew/revoke (re-)anchors the ledger');
527
+ }
528
+ const nowIso = new Date().toISOString();
529
+ const sid = clean(sessionId, 64);
530
+ const active = loaded.filter((g) => g.expiresAt > nowIso && (!sid || g.sessionId === sid));
531
+ const paths = [];
532
+ for (const g of active)
533
+ for (const p of g.paths)
534
+ if (!paths.includes(p))
535
+ paths.push(p);
536
+ return { grants: active, paths };
537
+ }
538
+ /** Record that a sandbox run USED its grants (the "who consented, when used" line).
539
+ * `netMode` is the network mode the run ACTUALLY engaged ('off' = the kernel
540
+ * egress boundary): the audit line is the consent record, so it must state
541
+ * the boundary that was really on. The legacy-env record states what the
542
+ * run actually OPENED (`legacyBound`, the resolved targets that were bound)
543
+ * separately from what plan time REFUSED (`legacyRefused`) — recording a
544
+ * refused entry as the run's allow would attribute authority the run never
545
+ * had, and the durable record must agree with the launch banner. */
546
+ export function auditSandboxUse(projectRoot, sessionId, used, legacyBound, legacyRefused, netMode) {
547
+ audit(projectRoot, {
548
+ ts: new Date().toISOString(),
549
+ action: 'sandbox-run',
550
+ sessionId: clean(sessionId, 64),
551
+ grantIds: used.grants.map((g) => g.id),
552
+ paths: used.paths,
553
+ netMode,
554
+ // The legacy env is still honored (backwards compatibility) but named in
555
+ // the audit: unrecorded authority is exactly what this ledger replaced.
556
+ // Control chars are cleaned here too — the audit line is operator-
557
+ // terminal output as much as JSONL.
558
+ unrecordedAllow: legacyBound.map((p) => clean(p, 512)),
559
+ unrecordedRefused: legacyRefused.map((p) => clean(p, 512)),
560
+ });
561
+ }
562
+ //# sourceMappingURL=sandbox-grants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-grants.js","sourceRoot":"","sources":["../../src/policies/sandbox-grants.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Implementation-State Manifest — a machine-generated snapshot of what this
3
3
  * project IS and where its implementation actually stands, derived from
4
- * ground truth (package.json, CHANGELOG, git branch, .factory registries)
4
+ * ground truth (package.json, git log, git branch, .factory registries)
5
5
  * rather than hand-maintained docs that drift.
6
6
  *
7
7
  * Written to `.uap/state-manifest.json`; the reactor injects a one-line
@@ -14,7 +14,9 @@ export interface StateManifest {
14
14
  version: string;
15
15
  generatedAt: string;
16
16
  branch?: string;
17
- /** Latest CHANGELOG headings, newest first — the real "what shipped lately". */
17
+ /** Latest commit subjects, newest first — the real "what shipped lately".
18
+ * UNTRUSTED input: commit subjects are contributor-authored text; they are
19
+ * stripped + single-lined at ingest and re-stripped at the digest boundary. */
18
20
  recentChanges: string[];
19
21
  /** Registry counts when the project has them (skills/droids/patterns). */
20
22
  counts?: Record<string, number>;
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/state/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAMD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AA4BD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CA6C/E;AAED,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAiB5E;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAW/E;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAe9D"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/state/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;mFAE+E;IAC/E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAMD,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAExD;AAoDD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAkC/E;AAED,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAiB5E;AAED,qEAAqE;AACrE,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAW/E;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,CAmB9D"}