loki-mode 7.129.5 → 8.0.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 (75) hide show
  1. package/README.md +121 -9
  2. package/SKILL.md +66 -5
  3. package/VERSION +1 -1
  4. package/autonomy/app-runner.sh +37 -0
  5. package/autonomy/completion-council.sh +862 -28
  6. package/autonomy/council-v2.sh +39 -0
  7. package/autonomy/crash.sh +3 -2
  8. package/autonomy/grill.sh +42 -0
  9. package/autonomy/hooks/validate-bash.sh +301 -4
  10. package/autonomy/lib/cockpit-render.sh +8 -2
  11. package/autonomy/lib/cr-rematerialize.py +101 -4
  12. package/autonomy/lib/deadline.py +957 -0
  13. package/autonomy/lib/dependency-setup.sh +205 -0
  14. package/autonomy/lib/done-recognition.sh +45 -0
  15. package/autonomy/lib/efficiency_cost.py +13 -6
  16. package/autonomy/lib/no_mock_scan.py +793 -0
  17. package/autonomy/lib/prd-enrich.sh +42 -0
  18. package/autonomy/lib/proof-analytics-props.py +69 -0
  19. package/autonomy/lib/proof-generator.py +326 -92
  20. package/autonomy/lib/proof-template.html +15 -12
  21. package/autonomy/lib/proof-verify.py +169 -105
  22. package/autonomy/lib/requirements_contract.py +848 -0
  23. package/autonomy/lib/sdk-mode.sh +103 -0
  24. package/autonomy/lib/secret-scan.sh +139 -0
  25. package/autonomy/lib/spec-expand.sh +208 -0
  26. package/autonomy/lib/tree_digest.py +266 -0
  27. package/autonomy/lib/voter-agents.sh +58 -8
  28. package/autonomy/lib/workspace_diff.py +141 -0
  29. package/autonomy/loki +196 -17
  30. package/autonomy/playwright-verify.sh +501 -0
  31. package/autonomy/prd-checklist.sh +17 -8
  32. package/autonomy/provider-offer.sh +111 -0
  33. package/autonomy/run.sh +4587 -671
  34. package/autonomy/sandbox.sh +42 -0
  35. package/autonomy/spec-interrogation.sh +148 -5
  36. package/autonomy/spec.sh +118 -1
  37. package/autonomy/telemetry.sh +133 -7
  38. package/autonomy/verify.sh +107 -0
  39. package/bin/loki +110 -3
  40. package/completions/_loki +10 -0
  41. package/completions/loki.bash +1 -1
  42. package/dashboard/__init__.py +1 -1
  43. package/dashboard/audit.py +96 -7
  44. package/dashboard/build_supervisor.py +1619 -0
  45. package/dashboard/control.py +8 -0
  46. package/dashboard/prompt_optimizer.py +7 -0
  47. package/dashboard/server.py +577 -22
  48. package/dashboard/static/trust.html +1 -1
  49. package/docs/ARCHITECTURE-OVERVIEW.md +207 -0
  50. package/docs/AUTONOMI-ECOSYSTEM.md +107 -0
  51. package/docs/INSTALLATION.md +19 -2
  52. package/docs/MODEL-EQUIVALENCE-HARNESS-PLAN.md +70 -0
  53. package/docs/PLANS-INDEX.md +33 -0
  54. package/docs/PRIVACY.md +29 -1
  55. package/docs/SIGNED-RECEIPTS.md +102 -0
  56. package/docs/SONNET5-DEFAULT-PLAN.md +1 -1
  57. package/docs/V8-ACCEPTANCE-TRIAGE-2026-07-24.md +114 -0
  58. package/docs/V8-AGENT-SDK-PLAN.md +692 -0
  59. package/docs/V8-COMPLEXITY-AUDIT-2026-07-24.md +45 -0
  60. package/docs/V8-MAJOR-RELEASE-PLAN.md +137 -0
  61. package/docs/V8-OVERNIGHT-PLAN-2026-07-25.md +82 -0
  62. package/docs/V8-RUNTIME-TRUTH-2026-07-25.md +232 -0
  63. package/docs/V8-SDK-RESEARCH-RAW.md +129 -0
  64. package/docs/alternative-installations.md +4 -4
  65. package/loki-ts/dist/loki.js +674 -285
  66. package/loki-ts/package.json +2 -0
  67. package/mcp/__init__.py +1 -1
  68. package/package.json +7 -6
  69. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  70. package/providers/claude.sh +75 -0
  71. package/providers/codex.sh +85 -13
  72. package/references/sdk-mode.md +106 -0
  73. package/skills/model-selection.md +1 -1
  74. package/skills/providers.md +42 -0
  75. package/skills/quality-gates.md +22 -0
@@ -0,0 +1,1619 @@
1
+ #!/usr/bin/env python3
2
+ """Durable supervisor for dashboard-started workspace builds."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ import ctypes
8
+ import fcntl
9
+ import hashlib
10
+ import importlib.util
11
+ import json
12
+ import os
13
+ import re
14
+ import signal
15
+ import socket
16
+ import subprocess
17
+ import sys
18
+ import tempfile
19
+ import time
20
+ import uuid
21
+ from contextlib import contextmanager
22
+ from datetime import datetime, timezone
23
+ from pathlib import Path
24
+ from typing import Any, Iterator
25
+
26
+ from autonomy.lib import deadline as process_deadline
27
+
28
+
29
+ SCHEMA_VERSION = 1
30
+ _DARWIN_LIBPROC: Any = None
31
+ _PROOF_VERIFIER: Any = None
32
+
33
+ _CHILD_ENV_NAMES = {
34
+ "CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC",
35
+ "DISABLE_AUTOUPDATER",
36
+ "DISABLE_TELEMETRY",
37
+ "HOME",
38
+ "LANG",
39
+ "LC_ALL",
40
+ "LC_CTYPE",
41
+ "LOGNAME",
42
+ "NO_COLOR",
43
+ "SHELL",
44
+ "TERM",
45
+ "USER",
46
+ }
47
+ _CHILD_LOKI_ENV_NAMES = {
48
+ "LOKI_ADVISOR_MODEL",
49
+ "LOKI_APP_RUNNER",
50
+ "LOKI_AUTONOMY_OVERRIDE",
51
+ "LOKI_BUILD_PROFILE",
52
+ "LOKI_CLAUDE_EFFORT",
53
+ "LOKI_CLAUDE_MODEL_DEVELOPMENT",
54
+ "LOKI_CLAUDE_MODEL_FAST",
55
+ "LOKI_CLAUDE_MODEL_PLANNING",
56
+ "LOKI_CLAUDE_TASK_BUDGET",
57
+ "LOKI_CODEX_MODEL",
58
+ "LOKI_CODEX_REASONING_EFFORT",
59
+ "LOKI_CODEX_WEB_SEARCH",
60
+ "LOKI_COMPLEXITY",
61
+ "LOKI_COUNCIL_ENABLED",
62
+ "LOKI_COUNCIL_SIZE",
63
+ "LOKI_COUNCIL_TIMEOUT_MS",
64
+ "LOKI_ENTERPRISE_AUTH",
65
+ "LOKI_FIRST_PASS_EXCELLENCE",
66
+ "LOKI_GEMINI_MODEL",
67
+ "LOKI_HOST_GUARD",
68
+ "LOKI_HOST_GUARD_SETTINGS_JSON",
69
+ "LOKI_LEGACY_BASH",
70
+ "LOKI_MAX_ITERATIONS",
71
+ "LOKI_NO_SESSION_PERSIST",
72
+ "LOKI_PARTIAL_MESSAGES",
73
+ "LOKI_PROMPT_INJECTION",
74
+ "LOKI_SDK_LOOP",
75
+ "LOKI_SDK_MODE",
76
+ "LOKI_SESSION_MODEL",
77
+ "LOKI_SETTING_SOURCES",
78
+ "LOKI_SKILL_DIR",
79
+ "LOKI_SPEC_SHA256",
80
+ "LOKI_SPEC_CONTRADICTION_FASTFAIL",
81
+ "LOKI_WORKSPACE_ROOTS",
82
+ }
83
+ _SYSTEM_RUNTIME_READ_PATHS = (
84
+ "/System",
85
+ "/usr",
86
+ "/bin",
87
+ "/sbin",
88
+ "/Library/Apple",
89
+ "/private/etc",
90
+ "/private/var/db/timezone",
91
+ "/private/var/select",
92
+ )
93
+ _SYSTEM_CHILD_PATHS = ("/usr/bin", "/bin", "/usr/sbin", "/sbin")
94
+ _PROVIDER_AUTH_COMMANDS = {
95
+ "claude": ("claude", "auth", "status"),
96
+ "codex": ("codex", "login", "status"),
97
+ "cline": ("cline", "auth", "status"),
98
+ }
99
+ _PROVIDER_READINESS_COMMANDS = {
100
+ "claude": (
101
+ "claude",
102
+ "--dangerously-skip-permissions",
103
+ "--model",
104
+ "haiku",
105
+ "--setting-sources",
106
+ "",
107
+ "--disallowedTools",
108
+ "Task",
109
+ "--tools",
110
+ "Bash",
111
+ "--effort",
112
+ "low",
113
+ "-p",
114
+ "Use the Bash tool to run printf ready. After it succeeds, reply with exactly OK.",
115
+ "--output-format",
116
+ "json",
117
+ ),
118
+ }
119
+ _PROVIDER_AUTH_ACTIONS = {
120
+ "claude": "Run 'claude login', unlock the macOS login keychain, then retry.",
121
+ "codex": "Run 'codex login', then retry.",
122
+ "cline": "Run 'cline auth', then retry.",
123
+ "aider": "Configure a provider with a non-interactive auth status command.",
124
+ }
125
+
126
+
127
+ class _DarwinProcBsdInfo(ctypes.Structure):
128
+ _fields_ = [
129
+ ("pbi_flags", ctypes.c_uint32),
130
+ ("pbi_status", ctypes.c_uint32),
131
+ ("pbi_xstatus", ctypes.c_uint32),
132
+ ("pbi_pid", ctypes.c_uint32),
133
+ ("pbi_ppid", ctypes.c_uint32),
134
+ ("pbi_uid", ctypes.c_uint32),
135
+ ("pbi_gid", ctypes.c_uint32),
136
+ ("pbi_ruid", ctypes.c_uint32),
137
+ ("pbi_rgid", ctypes.c_uint32),
138
+ ("pbi_svuid", ctypes.c_uint32),
139
+ ("pbi_svgid", ctypes.c_uint32),
140
+ ("rfu_1", ctypes.c_uint32),
141
+ ("pbi_comm", ctypes.c_char * 16),
142
+ ("pbi_name", ctypes.c_char * 32),
143
+ ("pbi_nfiles", ctypes.c_uint32),
144
+ ("pbi_pgid", ctypes.c_uint32),
145
+ ("pbi_pjobc", ctypes.c_uint32),
146
+ ("e_tdev", ctypes.c_uint32),
147
+ ("e_tpgid", ctypes.c_uint32),
148
+ ("pbi_nice", ctypes.c_int32),
149
+ ("pbi_start_tvsec", ctypes.c_uint64),
150
+ ("pbi_start_tvusec", ctypes.c_uint64),
151
+ ]
152
+
153
+
154
+ def utc_now() -> str:
155
+ return datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
156
+
157
+
158
+ def validate_execution_id(raw: str) -> str:
159
+ """Return the canonical UUID used as the durable execution identity."""
160
+ try:
161
+ return str(uuid.UUID(str(raw)))
162
+ except (ValueError, TypeError, AttributeError) as exc:
163
+ raise ValueError("request_id must be a valid UUID") from exc
164
+
165
+
166
+ def executions_root() -> Path:
167
+ base = Path(os.environ.get("LOKI_DATA_DIR", "~/.loki")).expanduser()
168
+ return base.resolve() / "dashboard" / "builds"
169
+
170
+
171
+ def execution_dir(execution_id: str) -> Path:
172
+ return executions_root() / validate_execution_id(execution_id)
173
+
174
+
175
+ def state_path(execution_id: str) -> Path:
176
+ return execution_dir(execution_id) / "state.json"
177
+
178
+
179
+ def _ensure_execution_dir(execution_id: str) -> Path:
180
+ path = execution_dir(execution_id)
181
+ path.mkdir(mode=0o700, parents=True, exist_ok=True)
182
+ return path
183
+
184
+
185
+ @contextmanager
186
+ def execution_lock(execution_id: str) -> Iterator[None]:
187
+ """Serialize state transitions and idempotent start decisions."""
188
+ directory = _ensure_execution_dir(execution_id)
189
+ lock_fd = os.open(str(directory / ".lock"), os.O_CREAT | os.O_RDWR, 0o600)
190
+ try:
191
+ fcntl.flock(lock_fd, fcntl.LOCK_EX)
192
+ yield
193
+ finally:
194
+ try:
195
+ fcntl.flock(lock_fd, fcntl.LOCK_UN)
196
+ finally:
197
+ os.close(lock_fd)
198
+
199
+
200
+ def read_state_unlocked(execution_id: str) -> dict[str, Any] | None:
201
+ try:
202
+ value = json.loads(state_path(execution_id).read_text(encoding="utf-8"))
203
+ except (OSError, json.JSONDecodeError, ValueError):
204
+ return None
205
+ return value if isinstance(value, dict) else None
206
+
207
+
208
+ def read_state(execution_id: str) -> dict[str, Any] | None:
209
+ """Read one complete state snapshot. Atomic writers prevent torn JSON."""
210
+ return read_state_unlocked(execution_id)
211
+
212
+
213
+ def _fsync_directory(path: Path) -> None:
214
+ try:
215
+ fd = os.open(str(path), os.O_RDONLY)
216
+ except OSError:
217
+ return
218
+ try:
219
+ os.fsync(fd)
220
+ except OSError:
221
+ pass
222
+ finally:
223
+ os.close(fd)
224
+
225
+
226
+ def _atomic_write(path: Path, payload: bytes, mode: int = 0o600) -> None:
227
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
228
+ fd, temporary = tempfile.mkstemp(
229
+ dir=str(path.parent), prefix=f".{path.name}.", suffix=".tmp"
230
+ )
231
+ try:
232
+ os.fchmod(fd, mode)
233
+ with os.fdopen(fd, "wb") as handle:
234
+ handle.write(payload)
235
+ handle.flush()
236
+ os.fsync(handle.fileno())
237
+ os.replace(temporary, path)
238
+ _fsync_directory(path.parent)
239
+ except BaseException:
240
+ try:
241
+ os.unlink(temporary)
242
+ except OSError:
243
+ pass
244
+ raise
245
+
246
+
247
+ def write_state_unlocked(execution_id: str, state: dict[str, Any]) -> None:
248
+ encoded = json.dumps(
249
+ state, sort_keys=True, indent=2, ensure_ascii=True
250
+ ).encode("utf-8") + b"\n"
251
+ _atomic_write(state_path(execution_id), encoded)
252
+
253
+
254
+ def update_state(execution_id: str, **updates: Any) -> dict[str, Any]:
255
+ with execution_lock(execution_id):
256
+ state = read_state_unlocked(execution_id) or {
257
+ "schema_version": SCHEMA_VERSION,
258
+ "execution_id": validate_execution_id(execution_id),
259
+ }
260
+ state.update(updates)
261
+ write_state_unlocked(execution_id, state)
262
+ return state
263
+
264
+
265
+ def write_spec_snapshot_unlocked(execution_id: str, content: bytes) -> Path:
266
+ path = execution_dir(execution_id) / "spec.md"
267
+ _atomic_write(path, content)
268
+ return path
269
+
270
+
271
+ def open_private_log(path: Path) -> Any:
272
+ """Open an append-only execution log with owner-only permissions."""
273
+ path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
274
+ fd = os.open(str(path), os.O_CREAT | os.O_WRONLY | os.O_APPEND, 0o600)
275
+ try:
276
+ os.fchmod(fd, 0o600)
277
+ return os.fdopen(fd, "ab", buffering=0)
278
+ except BaseException:
279
+ os.close(fd)
280
+ raise
281
+
282
+
283
+ def sha256_bytes(content: bytes) -> str:
284
+ return hashlib.sha256(content).hexdigest()
285
+
286
+
287
+ def _darwin_process_birth_token(pid: int) -> str:
288
+ """Read Darwin's microsecond process start time through libproc."""
289
+ if sys.platform != "darwin":
290
+ return ""
291
+ global _DARWIN_LIBPROC
292
+ try:
293
+ if _DARWIN_LIBPROC is None:
294
+ libproc = ctypes.CDLL("/usr/lib/libproc.dylib", use_errno=True)
295
+ libproc.proc_pidinfo.argtypes = [
296
+ ctypes.c_int,
297
+ ctypes.c_int,
298
+ ctypes.c_uint64,
299
+ ctypes.c_void_p,
300
+ ctypes.c_int,
301
+ ]
302
+ libproc.proc_pidinfo.restype = ctypes.c_int
303
+ _DARWIN_LIBPROC = libproc
304
+ info = _DarwinProcBsdInfo()
305
+ size = ctypes.sizeof(info)
306
+ read = _DARWIN_LIBPROC.proc_pidinfo(
307
+ int(pid), 3, 0, ctypes.byref(info), size
308
+ )
309
+ except (OSError, TypeError, ValueError, AttributeError):
310
+ return ""
311
+ if read != size or info.pbi_pid != int(pid):
312
+ return ""
313
+ return f"darwin:{info.pbi_start_tvsec}:{info.pbi_start_tvusec}"
314
+
315
+
316
+ def process_birth_token(pid: int) -> str:
317
+ """Return an OS process birth token suitable for PID reuse checks."""
318
+ try:
319
+ numeric_pid = int(pid)
320
+ except (TypeError, ValueError):
321
+ return ""
322
+ if numeric_pid <= 0:
323
+ return ""
324
+
325
+ proc_stat = Path(f"/proc/{numeric_pid}/stat")
326
+ try:
327
+ raw = proc_stat.read_text(encoding="utf-8", errors="replace")
328
+ fields = raw.rsplit(")", 1)[1].strip().split()
329
+ start_ticks = fields[19]
330
+ try:
331
+ boot_id = Path("/proc/sys/kernel/random/boot_id").read_text().strip()
332
+ except OSError:
333
+ boot_id = ""
334
+ return f"proc:{boot_id}:{start_ticks}"
335
+ except (OSError, IndexError):
336
+ pass
337
+
338
+ darwin_token = _darwin_process_birth_token(numeric_pid)
339
+ if darwin_token:
340
+ return darwin_token
341
+
342
+ try:
343
+ result = subprocess.run(
344
+ ["ps", "-o", "lstart=", "-p", str(numeric_pid)],
345
+ capture_output=True,
346
+ text=True,
347
+ timeout=5,
348
+ )
349
+ except (OSError, subprocess.SubprocessError):
350
+ return ""
351
+ started = (result.stdout or "").strip()
352
+ if result.returncode != 0 or not started:
353
+ return ""
354
+ return f"ps:{started}"
355
+
356
+
357
+ def _signal_safe_birth_token(token: Any) -> bool:
358
+ """True only for OS birth tokens precise enough to authorize a signal."""
359
+ if not isinstance(token, str):
360
+ return False
361
+ parts = token.split(":")
362
+ if parts[0] == "proc":
363
+ return len(parts) == 3 and bool(parts[1]) and parts[2].isdigit()
364
+ if parts[0] == "darwin":
365
+ return len(parts) == 3 and parts[1].isdigit() and parts[2].isdigit()
366
+ return False
367
+
368
+
369
+ def process_state(pid: int) -> str:
370
+ """Return the single-letter process state when the OS exposes it."""
371
+ try:
372
+ raw = Path(f"/proc/{int(pid)}/stat").read_text(
373
+ encoding="utf-8", errors="replace"
374
+ )
375
+ return raw.rsplit(")", 1)[1].strip().split()[0]
376
+ except (OSError, ValueError, IndexError):
377
+ pass
378
+ try:
379
+ result = subprocess.run(
380
+ ["ps", "-o", "state=", "-p", str(int(pid))],
381
+ capture_output=True,
382
+ text=True,
383
+ timeout=5,
384
+ )
385
+ except (OSError, ValueError, subprocess.SubprocessError):
386
+ return ""
387
+ return (result.stdout or "").strip()[:1]
388
+
389
+
390
+ def process_identity_matches(pid: Any, birth_token: Any) -> bool:
391
+ """Fail closed unless the live PID has the recorded birth token."""
392
+ if not isinstance(birth_token, str) or not birth_token:
393
+ return False
394
+ try:
395
+ numeric_pid = int(pid)
396
+ os.kill(numeric_pid, 0)
397
+ except (TypeError, ValueError, OSError):
398
+ return False
399
+ if process_state(numeric_pid) == "Z":
400
+ return False
401
+ return process_birth_token(numeric_pid) == birth_token
402
+
403
+
404
+ def _owned_runner_group_matches(
405
+ runner_pid: Any,
406
+ runner_pgid: Any,
407
+ runner_sid: Any,
408
+ runner_birth_token: Any,
409
+ ) -> bool:
410
+ """Verify the exact session leader before signaling its whole group."""
411
+ try:
412
+ pid = int(runner_pid)
413
+ pgid = int(runner_pgid)
414
+ sid = int(runner_sid)
415
+ except (TypeError, ValueError):
416
+ return False
417
+ if pid <= 1 or pid != pgid or pid != sid:
418
+ return False
419
+ if not _signal_safe_birth_token(runner_birth_token):
420
+ return False
421
+ if not process_identity_matches(pid, runner_birth_token):
422
+ return False
423
+ try:
424
+ return os.getpgid(pid) == pgid and os.getsid(pid) == sid
425
+ except OSError:
426
+ return False
427
+
428
+
429
+ def _signal_owned_runner_group(
430
+ runner_pid: Any,
431
+ runner_pgid: Any,
432
+ runner_sid: Any,
433
+ runner_birth_token: Any,
434
+ signum: int,
435
+ ) -> str:
436
+ """Signal the runner group or return a fail-closed error string."""
437
+ if not _owned_runner_group_matches(
438
+ runner_pid, runner_pgid, runner_sid, runner_birth_token
439
+ ):
440
+ return "Runner group identity changed or is imprecise; signal refused"
441
+ try:
442
+ os.killpg(int(runner_pgid), signum)
443
+ except ProcessLookupError:
444
+ return ""
445
+ except OSError as exc:
446
+ return f"Runner group signal failed: {type(exc).__name__}"
447
+ return ""
448
+
449
+
450
+ def empty_descendant_outcome() -> dict[str, Any]:
451
+ return {
452
+ "checked": False,
453
+ "detected": False,
454
+ "quiescent": False,
455
+ "term_sent": [],
456
+ "kill_sent": [],
457
+ "remaining_pids": [],
458
+ "error": "",
459
+ }
460
+
461
+
462
+ def _load_tree_digest() -> Any:
463
+ lib_dir = Path(__file__).resolve().parents[1] / "autonomy" / "lib"
464
+ if str(lib_dir) not in sys.path:
465
+ sys.path.insert(0, str(lib_dir))
466
+ from tree_digest import compute_tree_digest
467
+
468
+ return compute_tree_digest
469
+
470
+
471
+ def _load_proof_verifier() -> Any:
472
+ """Load the canonical verifier despite its hyphenated filename."""
473
+ global _PROOF_VERIFIER
474
+ if _PROOF_VERIFIER is not None:
475
+ return _PROOF_VERIFIER
476
+ path = (
477
+ Path(__file__).resolve().parents[1]
478
+ / "autonomy"
479
+ / "lib"
480
+ / "proof-verify.py"
481
+ )
482
+ spec = importlib.util.spec_from_file_location("loki_proof_verify", path)
483
+ if spec is None or spec.loader is None:
484
+ raise ImportError("canonical proof verifier could not be loaded")
485
+ module = importlib.util.module_from_spec(spec)
486
+ spec.loader.exec_module(module)
487
+ _PROOF_VERIFIER = module
488
+ return module
489
+
490
+
491
+ def empty_integrity_result(reason: str = "proof integrity not checked") -> dict[str, Any]:
492
+ return {
493
+ "hash_ok": False,
494
+ "gpg_ok": "n/a",
495
+ "generator_trusted": True,
496
+ "headline_consistent": None,
497
+ "degraded": [],
498
+ "reason": reason,
499
+ "ok": False,
500
+ }
501
+
502
+
503
+ def empty_proof_binding(
504
+ execution_id: str, reason: str = "proof not present"
505
+ ) -> dict[str, Any]:
506
+ return {
507
+ "present": False,
508
+ "run_id": "",
509
+ "headline": "",
510
+ "tree_sha256": "",
511
+ "document_sha256": "",
512
+ "proof_path": "",
513
+ "proof_url": f"/api/control/builds/{execution_id}/proof",
514
+ "snapshotted": False,
515
+ "snapshot_error": "",
516
+ "tree_fields_agree": False,
517
+ "execution": {},
518
+ "failed_quality_gates": [],
519
+ "integrity": empty_integrity_result(reason),
520
+ "bound": False,
521
+ }
522
+
523
+
524
+ def _read_run_id(loki_dir: Path) -> str:
525
+ try:
526
+ value = (loki_dir / "state" / "trust-run-id").read_text(
527
+ encoding="utf-8"
528
+ ).strip()
529
+ except OSError:
530
+ return ""
531
+ if not value.startswith("run-") or "/" in value or "\\" in value:
532
+ return ""
533
+ return value
534
+
535
+
536
+ def _proof_binding(
537
+ execution_id: str, loki_dir: Path, run_id: str, tree_sha256: str
538
+ ) -> dict[str, Any]:
539
+ """Snapshot and bind the exact final proof outside the tenant workspace."""
540
+ result = empty_proof_binding(execution_id)
541
+ if not run_id:
542
+ return result
543
+ proof_path = loki_dir / "proofs" / run_id / "proof.json"
544
+ try:
545
+ raw = proof_path.read_bytes()
546
+ except OSError:
547
+ return result
548
+ snapshot_path = execution_dir(execution_id) / "proof.json"
549
+ result["document_sha256"] = sha256_bytes(raw)
550
+ try:
551
+ _atomic_write(snapshot_path, raw, mode=0o400)
552
+ except OSError as exc:
553
+ result["snapshot_error"] = f"{type(exc).__name__}: snapshot write failed"
554
+ else:
555
+ result["proof_path"] = str(snapshot_path)
556
+ result["snapshotted"] = True
557
+ try:
558
+ proof = json.loads(raw.decode("utf-8"))
559
+ except (UnicodeDecodeError, json.JSONDecodeError):
560
+ return result
561
+ if not isinstance(proof, dict):
562
+ return result
563
+ try:
564
+ integrity = _load_proof_verifier().verify_integrity(proof)
565
+ except Exception as exc:
566
+ integrity = empty_integrity_result(
567
+ f"Canonical integrity verifier failed: {type(exc).__name__}"
568
+ )
569
+ if not isinstance(integrity, dict):
570
+ integrity = empty_integrity_result(
571
+ "Canonical integrity verifier returned an invalid result"
572
+ )
573
+ facts = proof.get("facts") if isinstance(proof.get("facts"), dict) else {}
574
+ git = facts.get("git") if isinstance(facts.get("git"), dict) else {}
575
+ honesty = (
576
+ proof.get("honesty") if isinstance(proof.get("honesty"), dict) else {}
577
+ )
578
+ execution = (
579
+ facts.get("execution")
580
+ if isinstance(facts.get("execution"), dict)
581
+ else {}
582
+ )
583
+ quality_gates = (
584
+ facts.get("quality_gates")
585
+ if isinstance(facts.get("quality_gates"), list)
586
+ else []
587
+ )
588
+ failed_quality_gates = [
589
+ str(gate.get("name") or "")
590
+ for gate in quality_gates
591
+ if isinstance(gate, dict) and gate.get("status") == "failed"
592
+ ]
593
+ proof_run_id = str(proof.get("run_id") or "")
594
+ facts_tree = str(git.get("tree_sha256") or "")
595
+ top_tree = str(proof.get("tree_sha256") or "")
596
+ proof_tree = top_tree or facts_tree
597
+ tree_fields_agree = not (top_tree and facts_tree) or top_tree == facts_tree
598
+ headline = str(proof.get("headline") or honesty.get("headline") or "")
599
+ result.update(
600
+ present=True,
601
+ run_id=proof_run_id,
602
+ headline=headline,
603
+ tree_sha256=proof_tree,
604
+ tree_fields_agree=tree_fields_agree,
605
+ execution=execution,
606
+ failed_quality_gates=failed_quality_gates,
607
+ integrity=integrity,
608
+ bound=bool(
609
+ proof_run_id == run_id
610
+ and tree_sha256
611
+ and proof_tree == tree_sha256
612
+ and tree_fields_agree
613
+ and result["snapshotted"]
614
+ and integrity.get("ok") is True
615
+ and integrity.get("headline_consistent") is True
616
+ ),
617
+ )
618
+ return result
619
+
620
+
621
+ def _positive_float_env(name: str, default: float) -> float:
622
+ try:
623
+ value = float(os.environ.get(name, str(default)))
624
+ except (TypeError, ValueError):
625
+ return default
626
+ return value if value > 0 else default
627
+
628
+
629
+ def _enabled_env(name: str) -> bool:
630
+ return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"}
631
+
632
+
633
+ def _path_is_within(path: Path, root: Path) -> bool:
634
+ try:
635
+ path.relative_to(root)
636
+ except ValueError:
637
+ return False
638
+ return True
639
+
640
+
641
+ def _required_path(name: str, raw: str, *, directory: bool) -> Path:
642
+ value = raw.strip()
643
+ if not value or any(character in value for character in "\n\r\t"):
644
+ raise ValueError(f"{name} contains an invalid path")
645
+ candidate = Path(value).expanduser()
646
+ if not candidate.is_absolute():
647
+ raise ValueError(f"{name} paths must be absolute")
648
+ try:
649
+ resolved = candidate.resolve(strict=True)
650
+ except OSError as exc:
651
+ raise ValueError(f"{name} path does not exist") from exc
652
+ if resolved == Path(resolved.anchor):
653
+ raise ValueError(f"{name} must not include the filesystem root")
654
+ if directory and not resolved.is_dir():
655
+ raise ValueError(f"{name} paths must be directories")
656
+ return resolved
657
+
658
+
659
+ def _required_path_list(name: str, *, directories: bool) -> tuple[Path, ...]:
660
+ raw = os.environ.get(name, "")
661
+ values = raw.split(":")
662
+ if not raw.strip() or any(not value.strip() for value in values):
663
+ raise ValueError(f"{name} must be a non-empty path list")
664
+ paths = tuple(
665
+ _required_path(name, value, directory=directories) for value in values
666
+ )
667
+ # De-duplicate rather than reject. _required_path RESOLVES symlinks, so two
668
+ # genuinely different entries can legitimately collapse to one: on most
669
+ # Linux distributions /bin is a symlink to /usr/bin (and /sbin to /usr/sbin),
670
+ # while on macOS they are distinct directories. A caller listing the
671
+ # conventional four system bin paths is correct on both platforms, and
672
+ # rejecting it on Linux made this a platform-dependent failure that passes
673
+ # on a developer Mac and fails in CI.
674
+ #
675
+ # Order is preserved (dict.fromkeys, not set) because these become sandbox
676
+ # profile entries and a stable order keeps the generated profile
677
+ # byte-reproducible.
678
+ deduped = tuple(dict.fromkeys(paths))
679
+ return deduped
680
+
681
+
682
+ def _reserved_ports() -> tuple[int, ...]:
683
+ raw = os.environ.get("LOKI_HOST_RESERVED_PORTS", "")
684
+ values = raw.split(",")
685
+ if not raw.strip() or any(not value.strip() for value in values):
686
+ raise ValueError("LOKI_HOST_RESERVED_PORTS must be a non-empty port list")
687
+ ports: list[int] = []
688
+ for raw_port in values:
689
+ value = raw_port.strip()
690
+ if not value.isascii() or not value.isdigit() or not 1 <= int(value) <= 65535:
691
+ raise ValueError("LOKI_HOST_RESERVED_PORTS contains an invalid port")
692
+ ports.append(int(value))
693
+ if len(set(ports)) != len(ports):
694
+ raise ValueError("LOKI_HOST_RESERVED_PORTS contains duplicate ports")
695
+ return tuple(ports)
696
+
697
+
698
+ def _host_boundary_config(run_sh: Path) -> dict[str, Any]:
699
+ """Normalize and validate every configured host boundary."""
700
+ run_sh = _required_path("run_sh", str(run_sh), directory=False)
701
+ engine_source = run_sh.parent.parent
702
+ protected_root = _required_path(
703
+ "LOKI_HOST_PROTECTED_ROOT",
704
+ os.environ.get("LOKI_HOST_PROTECTED_ROOT", ""),
705
+ directory=True,
706
+ )
707
+ workspace_roots = _required_path_list(
708
+ "LOKI_WORKSPACE_ROOTS", directories=True
709
+ )
710
+ runtime_read_paths = _required_path_list(
711
+ "LOKI_HOST_RUNTIME_READ_PATHS", directories=False
712
+ )
713
+ runtime_write_paths = _required_path_list(
714
+ "LOKI_HOST_RUNTIME_WRITE_PATHS", directories=True
715
+ )
716
+ child_path = _required_path_list("LOKI_HOST_CHILD_PATH", directories=True)
717
+
718
+ required_child_paths = tuple(
719
+ Path(value).resolve(strict=True)
720
+ for value in _SYSTEM_CHILD_PATHS
721
+ if Path(value).is_dir()
722
+ )
723
+ missing_child_paths = tuple(
724
+ path for path in required_child_paths if path not in child_path
725
+ )
726
+ if missing_child_paths:
727
+ missing = ":".join(str(path) for path in missing_child_paths)
728
+ raise ValueError(
729
+ "LOKI_HOST_CHILD_PATH must include the system command paths: "
730
+ f"{missing}"
731
+ )
732
+
733
+ for root in workspace_roots:
734
+ if root == protected_root or not _path_is_within(root, protected_root):
735
+ raise ValueError(
736
+ "LOKI_WORKSPACE_ROOTS must be strictly contained under "
737
+ "LOKI_HOST_PROTECTED_ROOT"
738
+ )
739
+ for path in (*runtime_read_paths, *runtime_write_paths):
740
+ if _path_is_within(path, protected_root) or _path_is_within(
741
+ protected_root, path
742
+ ):
743
+ raise ValueError(
744
+ "runtime paths must not overlap LOKI_HOST_PROTECTED_ROOT"
745
+ )
746
+
747
+ system_read_paths = tuple(
748
+ Path(value).resolve(strict=True)
749
+ for value in _SYSTEM_RUNTIME_READ_PATHS
750
+ if Path(value).exists()
751
+ )
752
+ executable_roots = (*system_read_paths, *runtime_read_paths)
753
+ for path in child_path:
754
+ if not any(_path_is_within(path, root) for root in executable_roots):
755
+ raise ValueError(
756
+ "LOKI_HOST_CHILD_PATH entries must be under an allowed read path"
757
+ )
758
+
759
+ return {
760
+ "workspace_roots": workspace_roots,
761
+ "engine_source": engine_source,
762
+ "protected_root": protected_root,
763
+ "runtime_read_paths": runtime_read_paths,
764
+ "runtime_write_paths": runtime_write_paths,
765
+ "system_read_paths": system_read_paths,
766
+ "child_path": child_path,
767
+ "ports": _reserved_ports(),
768
+ }
769
+
770
+
771
+ def host_confinement_identity(workspace_root: Path, run_sh: Path) -> dict[str, str]:
772
+ """Return the canonical config identity used by the engine manifest."""
773
+ config = _host_boundary_config(run_sh)
774
+ workspace_root = _required_path(
775
+ "workspace_root", str(workspace_root), directory=True
776
+ )
777
+ if workspace_root not in config["workspace_roots"]:
778
+ raise ValueError("workspace_root must exactly match LOKI_WORKSPACE_ROOTS")
779
+ payload = {
780
+ "workspace_roots": [str(path) for path in config["workspace_roots"]],
781
+ "protected_root": str(config["protected_root"]),
782
+ "runtime_read_paths": [str(path) for path in config["runtime_read_paths"]],
783
+ "runtime_write_paths": [str(path) for path in config["runtime_write_paths"]],
784
+ "child_path": [str(path) for path in config["child_path"]],
785
+ "reserved_ports": list(config["ports"]),
786
+ }
787
+ encoded = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode()
788
+ return {
789
+ "workspace_root": str(workspace_root),
790
+ "protected_root": payload["protected_root"],
791
+ "runtime_read_paths": ":".join(payload["runtime_read_paths"]),
792
+ "runtime_write_paths": ":".join(payload["runtime_write_paths"]),
793
+ "child_path": ":".join(payload["child_path"]),
794
+ "reserved_ports": ",".join(str(port) for port in config["ports"]),
795
+ "config_sha256": sha256_bytes(encoded),
796
+ }
797
+
798
+
799
+ def _host_confinement_config(
800
+ workspace: Path,
801
+ run_sh: Path,
802
+ spec: Path,
803
+ ) -> dict[str, Any]:
804
+ config = _host_boundary_config(run_sh)
805
+ workspace = _required_path("workspace", str(workspace), directory=True)
806
+ spec = _required_path("spec", str(spec), directory=False)
807
+ if not any(
808
+ workspace != root and _path_is_within(workspace, root)
809
+ for root in config["workspace_roots"]
810
+ ):
811
+ raise ValueError(
812
+ "LOKI host Seatbelt requires the workspace strictly under "
813
+ "LOKI_WORKSPACE_ROOTS"
814
+ )
815
+ engine_source = config["engine_source"]
816
+ if _path_is_within(workspace, engine_source) or _path_is_within(
817
+ engine_source, workspace
818
+ ):
819
+ raise ValueError("engine source and workspace boundaries must not overlap")
820
+ return {**config, "workspace": workspace, "spec": spec}
821
+
822
+
823
+ def _seatbelt_path_filter(path: Path) -> str:
824
+ kind = "subpath" if path.is_dir() else "literal"
825
+ return f"({kind} {json.dumps(str(path), ensure_ascii=True)})"
826
+
827
+
828
+ def _host_seatbelt_profile(
829
+ workspace: Path,
830
+ run_sh: Path,
831
+ spec: Path,
832
+ ) -> bytes:
833
+ """Build the macOS local-acceptance sandbox profile for one workspace."""
834
+ config = _host_confinement_config(workspace, run_sh, spec)
835
+ quote = lambda value: json.dumps(str(value), ensure_ascii=True)
836
+ lines = [
837
+ "(version 1)",
838
+ "(deny default)",
839
+ "(allow process-fork process-exec*)",
840
+ "(allow signal (target same-sandbox))",
841
+ "(allow process-info* (target same-sandbox))",
842
+ "(allow sysctl-read)",
843
+ "(allow mach-lookup)",
844
+ "(allow ipc-posix-shm ipc-posix-sem)",
845
+ "(allow network-outbound (remote ip))",
846
+ "(allow network-bind (local ip))",
847
+ "(deny network-outbound (remote unix-socket))",
848
+ '(allow network-outbound (literal "/private/var/run/mDNSResponder"))',
849
+ ";; Preexisting cross-boundary hardlinks share one kernel inode.",
850
+ ";; This path policy cannot prove which directory originally created it.",
851
+ ]
852
+ read_paths = {
853
+ *config["system_read_paths"],
854
+ *config["runtime_read_paths"],
855
+ config["engine_source"],
856
+ config["workspace"],
857
+ config["spec"],
858
+ }
859
+ metadata_paths = {Path("/"), Path("/var"), Path("/tmp"), Path("/etc"), Path("/dev")}
860
+ for path in (*read_paths, *config["runtime_write_paths"]):
861
+ metadata_paths.update(path.parents)
862
+ lines.append('(allow file-read-metadata file-read-data (literal "/"))')
863
+ for path in sorted(metadata_paths - {Path("/")}, key=str):
864
+ lines.append(
865
+ f"(allow file-read-metadata (literal {quote(path)}))"
866
+ )
867
+ for path in sorted(read_paths, key=str):
868
+ lines.append(
869
+ f"(allow file-read* file-map-executable {_seatbelt_path_filter(path)})"
870
+ )
871
+ write_paths = {*config["runtime_write_paths"], config["workspace"]}
872
+ for path in sorted(write_paths, key=str):
873
+ lines.append(
874
+ f"(allow file-read* file-write* file-map-executable "
875
+ f"{_seatbelt_path_filter(path)})"
876
+ )
877
+ for device in ("/dev/null", "/dev/random", "/dev/urandom", "/dev/zero"):
878
+ if Path(device).exists():
879
+ lines.append(
880
+ f"(allow file-read* file-write* (literal {quote(device)}))"
881
+ )
882
+ if Path("/dev/fd").exists():
883
+ lines.append('(allow file-read* file-write* (subpath "/dev/fd"))')
884
+ for port in config["ports"]:
885
+ lines.append(
886
+ f'(deny network-outbound (remote tcp "*:{port}"))'
887
+ )
888
+ lines.append(f'(deny network-bind (local tcp "*:{port}"))')
889
+ return ("\n".join(lines) + "\n").encode("utf-8")
890
+
891
+
892
+ def _child_environment(workspace: Path, loki_dir: Path) -> dict[str, str]:
893
+ """Pass only non-secret runtime configuration into the tenant process."""
894
+ child_env = {
895
+ name: os.environ[name]
896
+ for name in (_CHILD_ENV_NAMES | _CHILD_LOKI_ENV_NAMES)
897
+ if name in os.environ
898
+ }
899
+ child_env["PATH"] = os.environ.get(
900
+ "LOKI_HOST_CHILD_PATH", "/usr/bin:/bin:/usr/sbin:/sbin"
901
+ )
902
+ child_env["LOKI_OWN_SESSION"] = "1"
903
+ child_env["LOKI_TARGET_DIR"] = str(workspace)
904
+ child_env["LOKI_DIR"] = str(loki_dir)
905
+ child_env["LOKI_NO_SESSION_PERSIST"] = "1"
906
+ child_env["LOKI_SUPERVISED_BUILD"] = "1"
907
+ # Supervised builds are durable jobs. This activates run.sh's existing
908
+ # terminal exit contract so max-iteration and policy failures return 20
909
+ # instead of looking like successful process exits.
910
+ child_env["LOKI_DURABLE_STATE"] = "1"
911
+ child_env["CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC"] = "1"
912
+ child_env["DISABLE_AUTOUPDATER"] = "1"
913
+ child_env["DISABLE_TELEMETRY"] = "1"
914
+ runtime_root = loki_dir / "host-runtime"
915
+ for name in (
916
+ "tmp",
917
+ "cache",
918
+ "config",
919
+ "data",
920
+ "state",
921
+ "npm",
922
+ "bun",
923
+ "claude",
924
+ ):
925
+ (runtime_root / name).mkdir(mode=0o700, parents=True, exist_ok=True)
926
+ child_env["TMPDIR"] = str(runtime_root / "tmp")
927
+ child_env["XDG_CACHE_HOME"] = str(runtime_root / "cache")
928
+ child_env["XDG_CONFIG_HOME"] = str(runtime_root / "config")
929
+ child_env["XDG_DATA_HOME"] = str(runtime_root / "data")
930
+ child_env["XDG_STATE_HOME"] = str(runtime_root / "state")
931
+ child_env["npm_config_cache"] = str(runtime_root / "npm")
932
+ child_env["BUN_INSTALL_CACHE_DIR"] = str(runtime_root / "bun")
933
+ git_config = runtime_root / "gitconfig"
934
+ _atomic_write(git_config, b"[commit]\n\tgpgsign = false\n")
935
+ child_env["GIT_CONFIG_GLOBAL"] = str(git_config)
936
+ child_env["GIT_CONFIG_NOSYSTEM"] = "1"
937
+ return child_env
938
+
939
+
940
+ def _provider_auth_succeeded(
941
+ provider: str,
942
+ result: subprocess.CompletedProcess[str],
943
+ ) -> tuple[bool, str]:
944
+ """Classify auth status without returning provider output."""
945
+ if provider != "claude":
946
+ available = result.returncode == 0
947
+ return (
948
+ available,
949
+ "available"
950
+ if available
951
+ else "provider_auth_unavailable_in_confinement",
952
+ )
953
+ try:
954
+ payload = json.loads(result.stdout)
955
+ except (json.JSONDecodeError, TypeError):
956
+ return False, "provider_auth_status_invalid"
957
+ available = result.returncode == 0 and payload.get("loggedIn") is True
958
+ return (
959
+ available,
960
+ "available" if available else "provider_auth_unavailable_in_confinement",
961
+ )
962
+
963
+
964
+ def _provider_readiness_succeeded(
965
+ provider: str,
966
+ result: subprocess.CompletedProcess[str],
967
+ ) -> bool:
968
+ """Accept only a real, non-error provider response without returning it."""
969
+ if result.returncode != 0:
970
+ return False
971
+ if provider != "claude":
972
+ return True
973
+ try:
974
+ payload = json.loads(result.stdout)
975
+ except (json.JSONDecodeError, TypeError):
976
+ return False
977
+ response = str(payload.get("result", "")).strip().rstrip(".")
978
+ return payload.get("is_error") is False and response == "OK"
979
+
980
+
981
+ def confined_provider_auth_status(
982
+ provider: str,
983
+ workspace: Path,
984
+ run_sh: Path,
985
+ spec: Path,
986
+ ) -> dict[str, str | bool]:
987
+ """Prove provider auth and live inference at the exact child boundary."""
988
+ action = _PROVIDER_AUTH_ACTIONS.get(
989
+ provider,
990
+ "Authenticate the selected provider, then retry.",
991
+ )
992
+ if not _enabled_env("LOKI_HOST_SEATBELT"):
993
+ return {
994
+ "provider": provider,
995
+ "available": True,
996
+ "classification": "seatbelt_not_enabled",
997
+ "action": action,
998
+ }
999
+ command = _PROVIDER_AUTH_COMMANDS.get(provider)
1000
+ if command is None:
1001
+ return {
1002
+ "provider": provider,
1003
+ "available": False,
1004
+ "classification": "provider_auth_probe_unsupported",
1005
+ "action": action,
1006
+ }
1007
+ readiness_command = _PROVIDER_READINESS_COMMANDS.get(provider)
1008
+ if readiness_command is None:
1009
+ return {
1010
+ "provider": provider,
1011
+ "available": False,
1012
+ "classification": "provider_readiness_probe_unsupported",
1013
+ "action": action,
1014
+ }
1015
+
1016
+ workspace = workspace.resolve(strict=True)
1017
+ run_sh = run_sh.resolve(strict=True)
1018
+ spec = spec.resolve(strict=True)
1019
+ sandbox_exec = Path("/usr/bin/sandbox-exec")
1020
+ if sys.platform != "darwin" or not sandbox_exec.is_file():
1021
+ return {
1022
+ "provider": provider,
1023
+ "available": False,
1024
+ "classification": "provider_auth_confinement_unavailable",
1025
+ "action": action,
1026
+ }
1027
+
1028
+ loki_dir = workspace / ".loki"
1029
+ child_env = _child_environment(workspace, loki_dir)
1030
+ profile = _host_seatbelt_profile(workspace, run_sh, spec)
1031
+ profile_path = (
1032
+ loki_dir
1033
+ / "host-runtime"
1034
+ / f"provider-auth-{uuid.uuid4().hex}.sb"
1035
+ )
1036
+ _atomic_write(profile_path, profile)
1037
+ try:
1038
+ auth_result = subprocess.run(
1039
+ [str(sandbox_exec), "-f", str(profile_path), *command],
1040
+ cwd=str(workspace),
1041
+ env=child_env,
1042
+ capture_output=True,
1043
+ text=True,
1044
+ timeout=10,
1045
+ )
1046
+ except subprocess.TimeoutExpired:
1047
+ return {
1048
+ "provider": provider,
1049
+ "available": False,
1050
+ "classification": "provider_auth_probe_timeout",
1051
+ "action": action,
1052
+ }
1053
+ except OSError:
1054
+ return {
1055
+ "provider": provider,
1056
+ "available": False,
1057
+ "classification": "provider_cli_unavailable",
1058
+ "action": action,
1059
+ }
1060
+ finally:
1061
+ profile_path.unlink(missing_ok=True)
1062
+
1063
+ available, classification = _provider_auth_succeeded(provider, auth_result)
1064
+ if not available:
1065
+ return {
1066
+ "provider": provider,
1067
+ "available": False,
1068
+ "classification": classification,
1069
+ "action": action,
1070
+ }
1071
+
1072
+ profile_path = (
1073
+ loki_dir
1074
+ / "host-runtime"
1075
+ / f"provider-ready-{uuid.uuid4().hex}.sb"
1076
+ )
1077
+ _atomic_write(profile_path, profile)
1078
+ try:
1079
+ readiness_result = subprocess.run(
1080
+ [str(sandbox_exec), "-f", str(profile_path), *readiness_command],
1081
+ cwd=str(workspace),
1082
+ env=child_env,
1083
+ capture_output=True,
1084
+ text=True,
1085
+ timeout=20,
1086
+ )
1087
+ except subprocess.TimeoutExpired:
1088
+ return {
1089
+ "provider": provider,
1090
+ "available": False,
1091
+ "classification": "provider_readiness_probe_timeout",
1092
+ "action": action,
1093
+ }
1094
+ except OSError:
1095
+ return {
1096
+ "provider": provider,
1097
+ "available": False,
1098
+ "classification": "provider_cli_unavailable",
1099
+ "action": action,
1100
+ }
1101
+ finally:
1102
+ profile_path.unlink(missing_ok=True)
1103
+
1104
+ if not _provider_readiness_succeeded(provider, readiness_result):
1105
+ return {
1106
+ "provider": provider,
1107
+ "available": False,
1108
+ "classification": "provider_live_inference_unavailable",
1109
+ "action": action,
1110
+ }
1111
+ return {
1112
+ "provider": provider,
1113
+ "available": True,
1114
+ "classification": "available",
1115
+ "action": action,
1116
+ "live_inference_passed": True,
1117
+ }
1118
+
1119
+
1120
+ def smoke_probe_confined_provider_auth(
1121
+ workspace_root: Path,
1122
+ run_sh: Path,
1123
+ provider: str,
1124
+ ) -> dict[str, str | bool]:
1125
+ """Probe auth and live inference before advertising engine readiness."""
1126
+ workspace_root = _required_path(
1127
+ "workspace_root", str(workspace_root), directory=True
1128
+ )
1129
+ with tempfile.TemporaryDirectory(
1130
+ prefix=".provider-auth-probe-", dir=workspace_root
1131
+ ) as workspace_raw:
1132
+ workspace = Path(workspace_raw).resolve()
1133
+ spec = workspace / "spec.md"
1134
+ spec.write_text("provider auth capability probe", encoding="utf-8")
1135
+ return confined_provider_auth_status(provider, workspace, run_sh, spec)
1136
+
1137
+
1138
+ def smoke_probe_host_seatbelt(workspace_root: Path, run_sh: Path) -> dict[str, str]:
1139
+ """Prove the configured kernel profile before advertising confinement."""
1140
+ if sys.platform != "darwin":
1141
+ raise RuntimeError("LOKI host Seatbelt is available only on macOS")
1142
+ sandbox_exec = Path("/usr/bin/sandbox-exec")
1143
+ if not sandbox_exec.is_file() or not os.access(sandbox_exec, os.X_OK):
1144
+ raise RuntimeError("LOKI host Seatbelt requires /usr/bin/sandbox-exec")
1145
+
1146
+ workspace_root = _required_path(
1147
+ "workspace_root", str(workspace_root), directory=True
1148
+ )
1149
+ with tempfile.TemporaryDirectory(
1150
+ prefix=".seatbelt-probe-workspace-", dir=workspace_root
1151
+ ) as workspace_raw, tempfile.TemporaryDirectory(
1152
+ prefix=".seatbelt-probe-sibling-", dir=workspace_root
1153
+ ) as sibling_raw:
1154
+ workspace = Path(workspace_raw).resolve()
1155
+ sibling = Path(sibling_raw).resolve()
1156
+ spec = workspace / "spec.md"
1157
+ spec.write_text("seatbelt probe", encoding="utf-8")
1158
+ secret = sibling / "secret.txt"
1159
+ secret.write_text("must remain unreadable", encoding="utf-8")
1160
+ unix_socket_path = workspace / "probe.sock"
1161
+
1162
+ config = _host_confinement_config(workspace, run_sh, spec)
1163
+ probe_port = 0
1164
+ for port in config["ports"]:
1165
+ with socket.socket() as candidate:
1166
+ try:
1167
+ candidate.bind(("127.0.0.1", port))
1168
+ except OSError:
1169
+ continue
1170
+ probe_port = port
1171
+ break
1172
+ if not probe_port:
1173
+ raise RuntimeError("no reserved port was free for the Seatbelt probe")
1174
+
1175
+ profile = _host_seatbelt_profile(workspace, run_sh, spec)
1176
+ profile_path = workspace / "probe.sb"
1177
+ profile_path.write_bytes(profile)
1178
+ output_path = workspace / "probe.txt"
1179
+ probe_code = """
1180
+ import errno
1181
+ import os
1182
+ import signal
1183
+ import socket
1184
+ import subprocess
1185
+ import sys
1186
+
1187
+ output, sibling, unix_path, port = sys.argv[1:]
1188
+ open(output, "w", encoding="utf-8").write("confined")
1189
+ open("/etc/hosts", encoding="utf-8").read(1)
1190
+ child = subprocess.Popen(
1191
+ ["/bin/sh", "-c", "/bin/sleep 5 & wait"],
1192
+ stdin=subprocess.DEVNULL,
1193
+ stdout=subprocess.DEVNULL,
1194
+ stderr=subprocess.DEVNULL,
1195
+ preexec_fn=os.setpgrp,
1196
+ )
1197
+ os.killpg(child.pid, signal.SIGKILL)
1198
+ child.wait(timeout=2)
1199
+ try:
1200
+ open(sibling, encoding="utf-8").read(1)
1201
+ except OSError:
1202
+ pass
1203
+ else:
1204
+ raise SystemExit(41)
1205
+ with socket.socket(socket.AF_UNIX) as client:
1206
+ try:
1207
+ client.connect(unix_path)
1208
+ except OSError:
1209
+ pass
1210
+ else:
1211
+ raise SystemExit(42)
1212
+ with socket.socket() as listener:
1213
+ try:
1214
+ listener.bind(("127.0.0.1", int(port)))
1215
+ except OSError as exc:
1216
+ if exc.errno not in (errno.EACCES, errno.EPERM):
1217
+ raise
1218
+ else:
1219
+ raise SystemExit(43)
1220
+ """
1221
+ with socket.socket(socket.AF_UNIX) as unix_server:
1222
+ unix_server.bind(str(unix_socket_path))
1223
+ unix_server.listen(1)
1224
+ try:
1225
+ result = subprocess.run(
1226
+ [
1227
+ str(sandbox_exec),
1228
+ "-f",
1229
+ str(profile_path),
1230
+ "/usr/bin/python3",
1231
+ "-c",
1232
+ probe_code,
1233
+ str(output_path),
1234
+ str(secret),
1235
+ str(unix_socket_path),
1236
+ str(probe_port),
1237
+ ],
1238
+ cwd=str(workspace),
1239
+ env={
1240
+ "HOME": os.environ.get("HOME", "/var/empty"),
1241
+ "LANG": os.environ.get("LANG", "C"),
1242
+ "PATH": os.environ.get(
1243
+ "LOKI_HOST_CHILD_PATH", "/usr/bin:/bin:/usr/sbin:/sbin"
1244
+ ),
1245
+ "TMPDIR": str(workspace),
1246
+ },
1247
+ capture_output=True,
1248
+ text=True,
1249
+ timeout=15,
1250
+ )
1251
+ except subprocess.SubprocessError as exc:
1252
+ raise RuntimeError("Seatbelt kernel smoke probe did not finish") from exc
1253
+ if result.returncode != 0 or output_path.read_text(encoding="utf-8") != "confined":
1254
+ raise RuntimeError(
1255
+ f"Seatbelt kernel smoke probe failed with exit {result.returncode}"
1256
+ )
1257
+ return {
1258
+ **host_confinement_identity(workspace_root, run_sh),
1259
+ "probe_sha256": sha256_bytes(profile),
1260
+ }
1261
+
1262
+
1263
+ def _host_confined_args(
1264
+ execution_id: str,
1265
+ args: list[str],
1266
+ workspace: Path,
1267
+ run_sh: Path,
1268
+ spec: Path,
1269
+ ) -> tuple[list[str], dict[str, Any] | None]:
1270
+ """Wrap a controlled local build in macOS Seatbelt when explicitly armed."""
1271
+ if not _enabled_env("LOKI_HOST_SEATBELT"):
1272
+ return args, None
1273
+ if sys.platform != "darwin":
1274
+ raise RuntimeError("LOKI host Seatbelt is available only on macOS")
1275
+ sandbox_exec = Path("/usr/bin/sandbox-exec")
1276
+ if not sandbox_exec.is_file() or not os.access(sandbox_exec, os.X_OK):
1277
+ raise RuntimeError("LOKI host Seatbelt requires /usr/bin/sandbox-exec")
1278
+
1279
+ probe_sha256 = os.environ.get("LOKI_HOST_SEATBELT_PROBE_SHA256", "").strip()
1280
+ if not re.fullmatch(r"[0-9a-f]{64}", probe_sha256):
1281
+ raise RuntimeError("LOKI host Seatbelt kernel probe is missing or invalid")
1282
+ profile = _host_seatbelt_profile(workspace, run_sh, spec)
1283
+ profile_path = execution_dir(execution_id) / "host-seatbelt.sb"
1284
+ _atomic_write(profile_path, profile)
1285
+ confinement = {
1286
+ "kind": "macos-seatbelt",
1287
+ "profile_sha256": sha256_bytes(profile),
1288
+ "kernel_probe_sha256": probe_sha256,
1289
+ "known_limitations": [
1290
+ "preexisting-cross-boundary-hardlinks",
1291
+ "unrestricted-outbound-network",
1292
+ "provider-credential-store-visible-to-confined-build",
1293
+ "deprecated-macos-sandbox-exec",
1294
+ ],
1295
+ }
1296
+ return [str(sandbox_exec), "-f", str(profile_path), *args], confinement
1297
+
1298
+
1299
+ def run_supervisor(
1300
+ execution_id: str,
1301
+ run_sh: Path,
1302
+ workspace: Path,
1303
+ loki_dir: Path,
1304
+ spec: Path,
1305
+ provider: str,
1306
+ parallel: bool = False,
1307
+ ) -> int:
1308
+ """Run one foreground engine child and persist its exact terminal result."""
1309
+ execution_id = validate_execution_id(execution_id)
1310
+ run_sh = run_sh.resolve(strict=True)
1311
+ workspace = workspace.resolve(strict=True)
1312
+ loki_dir = loki_dir.resolve(strict=True)
1313
+ spec = spec.resolve(strict=True)
1314
+ supervisor_pid = os.getpid()
1315
+ update_state(
1316
+ execution_id,
1317
+ supervisor_pid=supervisor_pid,
1318
+ supervisor_birth_token=process_birth_token(supervisor_pid),
1319
+ )
1320
+
1321
+ args = [str(run_sh), "--provider", provider]
1322
+ if parallel:
1323
+ args.append("--parallel")
1324
+ args.append(str(spec))
1325
+
1326
+ child_env = _child_environment(workspace, loki_dir)
1327
+ # A reused brownfield workspace may already contain an older run identity.
1328
+ # Only a value minted after this execution starts may bind its receipt.
1329
+ baseline_run_id = _read_run_id(loki_dir)
1330
+
1331
+ runner_log = execution_dir(execution_id) / "runner.log"
1332
+ try:
1333
+ args, confinement = _host_confined_args(
1334
+ execution_id,
1335
+ args,
1336
+ workspace,
1337
+ run_sh,
1338
+ spec,
1339
+ )
1340
+ if confinement is not None:
1341
+ update_state(execution_id, confinement=confinement)
1342
+ log_handle = open_private_log(runner_log)
1343
+ process, lineage_tracker = process_deadline.spawn_tracked(
1344
+ args,
1345
+ cwd=str(workspace),
1346
+ env=child_env,
1347
+ stdout=log_handle,
1348
+ stderr=subprocess.STDOUT,
1349
+ )
1350
+ except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as exc:
1351
+ try:
1352
+ log_handle.close()
1353
+ except (NameError, OSError):
1354
+ pass
1355
+ finished_at = utc_now()
1356
+ update_state(
1357
+ execution_id,
1358
+ state="exited",
1359
+ exited_at=finished_at,
1360
+ finished_at=finished_at,
1361
+ returncode=None,
1362
+ exit_code=None,
1363
+ signal=None,
1364
+ termination_reason="launch_failed",
1365
+ launch_error=str(exc),
1366
+ proof=empty_proof_binding(execution_id, "runner launch failed"),
1367
+ )
1368
+ return 127
1369
+
1370
+ runner_pid = process.pid
1371
+ # start_new_session=True makes the child both session and process-group
1372
+ # leader before exec. These IDs remain authoritative after the leader exits.
1373
+ runner_pgid = runner_pid
1374
+ runner_sid = runner_pid
1375
+ runner_birth_token = process_birth_token(runner_pid)
1376
+ update_state(
1377
+ execution_id,
1378
+ state="running",
1379
+ started_at=utc_now(),
1380
+ runner_pid=runner_pid,
1381
+ runner_pgid=runner_pgid,
1382
+ runner_sid=runner_sid,
1383
+ runner_birth_token=runner_birth_token,
1384
+ runner_log=str(runner_log),
1385
+ )
1386
+
1387
+ received_signal = 0
1388
+
1389
+ def _request_stop(signum: int, _frame: Any) -> None:
1390
+ nonlocal received_signal
1391
+ received_signal = signum
1392
+
1393
+ previous_handlers: dict[int, Any] = {}
1394
+ for signum in (signal.SIGTERM, signal.SIGINT):
1395
+ previous_handlers[signum] = signal.signal(signum, _request_stop)
1396
+
1397
+ stop_sent_at: float | None = None
1398
+ stop_refused = False
1399
+ kill_sent = False
1400
+ stop_grace = _positive_float_env("LOKI_BUILD_STOP_GRACE_SECONDS", 10.0)
1401
+ poll_interval = _positive_float_env("LOKI_BUILD_SUPERVISOR_POLL_SECONDS", 0.2)
1402
+ cached_run_id = ""
1403
+ lineage_error = ""
1404
+ try:
1405
+ while process.poll() is None:
1406
+ if not lineage_error:
1407
+ try:
1408
+ lineage_tracker.refresh()
1409
+ except (OSError, PermissionError, RuntimeError) as exc:
1410
+ lineage_error = (
1411
+ "Runner lineage tracking failed: " + type(exc).__name__
1412
+ )
1413
+ update_state(execution_id, stop_error=lineage_error)
1414
+ state = read_state(execution_id) or {}
1415
+ if not cached_run_id:
1416
+ candidate_run_id = _read_run_id(loki_dir)
1417
+ if candidate_run_id and candidate_run_id != baseline_run_id:
1418
+ cached_run_id = candidate_run_id
1419
+ update_state(execution_id, run_id=cached_run_id)
1420
+ stop_requested = bool(
1421
+ state.get("stop_requested_at") or received_signal or lineage_error
1422
+ )
1423
+ if stop_requested and stop_sent_at is None and not stop_refused:
1424
+ signal_error = _signal_owned_runner_group(
1425
+ runner_pid,
1426
+ runner_pgid,
1427
+ runner_sid,
1428
+ runner_birth_token,
1429
+ signal.SIGTERM,
1430
+ )
1431
+ if signal_error:
1432
+ update_state(
1433
+ execution_id,
1434
+ stop_error=signal_error,
1435
+ )
1436
+ stop_refused = True
1437
+ else:
1438
+ stop_sent_at = time.monotonic()
1439
+ update_state(execution_id, stop_signal_sent_at=utc_now())
1440
+ elif (
1441
+ stop_sent_at is not None
1442
+ and time.monotonic() - stop_sent_at >= stop_grace
1443
+ and not kill_sent
1444
+ ):
1445
+ signal_error = _signal_owned_runner_group(
1446
+ runner_pid,
1447
+ runner_pgid,
1448
+ runner_sid,
1449
+ runner_birth_token,
1450
+ signal.SIGKILL,
1451
+ )
1452
+ if signal_error:
1453
+ update_state(
1454
+ execution_id,
1455
+ stop_error=signal_error,
1456
+ )
1457
+ kill_sent = True
1458
+ time.sleep(poll_interval)
1459
+ returncode = process.wait()
1460
+ finally:
1461
+ log_handle.close()
1462
+ for signum, handler in previous_handlers.items():
1463
+ signal.signal(signum, handler)
1464
+
1465
+ descendant_grace = _positive_float_env(
1466
+ "LOKI_BUILD_DESCENDANT_GRACE_SECONDS", 2.0
1467
+ )
1468
+ if lineage_error:
1469
+ descendants = empty_descendant_outcome()
1470
+ descendants["error"] = lineage_error
1471
+ else:
1472
+ try:
1473
+ descendants = process_deadline.reconcile_lineage(
1474
+ process, lineage_tracker, descendant_grace
1475
+ )
1476
+ except (OSError, PermissionError, RuntimeError) as exc:
1477
+ descendants = empty_descendant_outcome()
1478
+ descendants["error"] = (
1479
+ "Runner lineage reconciliation failed: " + type(exc).__name__
1480
+ )
1481
+ lineage_tracker.close()
1482
+ abnormal_descendants = bool(descendants.get("detected") or descendants.get("error"))
1483
+ if abnormal_descendants:
1484
+ run_id = ""
1485
+ tree_sha256 = ""
1486
+ proof = empty_proof_binding(
1487
+ execution_id,
1488
+ "proof not captured because runner descendants survived direct exit",
1489
+ )
1490
+ else:
1491
+ final_candidate_run_id = _read_run_id(loki_dir)
1492
+ run_id = cached_run_id or (
1493
+ final_candidate_run_id
1494
+ if final_candidate_run_id and final_candidate_run_id != baseline_run_id
1495
+ else ""
1496
+ )
1497
+ # Snapshot the exact proof first, then derive the returned source tree.
1498
+ proof = _proof_binding(execution_id, loki_dir, run_id, "")
1499
+ tree_sha256 = _load_tree_digest()(workspace)
1500
+ integrity = (
1501
+ proof.get("integrity")
1502
+ if isinstance(proof.get("integrity"), dict)
1503
+ else {}
1504
+ )
1505
+ proof["bound"] = bool(
1506
+ proof.get("present")
1507
+ and proof.get("snapshotted")
1508
+ and proof.get("run_id") == run_id
1509
+ and tree_sha256
1510
+ and proof.get("tree_sha256") == tree_sha256
1511
+ and proof.get("tree_fields_agree")
1512
+ and integrity.get("ok") is True
1513
+ and integrity.get("headline_consistent") is True
1514
+ )
1515
+ stopped = bool(stop_sent_at is not None)
1516
+ if descendants.get("detected"):
1517
+ reason = (
1518
+ "descendants_terminated"
1519
+ if descendants.get("quiescent")
1520
+ else "descendants_uncontained"
1521
+ )
1522
+ elif descendants.get("error"):
1523
+ reason = "descendant_check_failed"
1524
+ elif stopped:
1525
+ reason = "stopped"
1526
+ elif (
1527
+ returncode == 0
1528
+ and proof.get("bound") is True
1529
+ and proof.get("headline") == "VERIFIED"
1530
+ and (proof.get("execution") or {}).get("exit_code") == 0
1531
+ and (proof.get("execution") or {}).get("run_status") in {
1532
+ "deterministic_gates_passed",
1533
+ "council_approved",
1534
+ "council_force_approved",
1535
+ "completion_promise_fulfilled",
1536
+ "reuse_already_satisfied",
1537
+ }
1538
+ and not proof.get("failed_quality_gates")
1539
+ ):
1540
+ reason = "completed"
1541
+ elif returncode in (0, 20):
1542
+ reason = "verification_failed"
1543
+ else:
1544
+ reason = "crashed"
1545
+ exit_code = returncode if returncode >= 0 else None
1546
+ exit_signal = -returncode if returncode < 0 else None
1547
+ finished_at = utc_now()
1548
+ update_state(
1549
+ execution_id,
1550
+ state="exited",
1551
+ exited_at=finished_at,
1552
+ finished_at=finished_at,
1553
+ returncode=returncode,
1554
+ exit_code=exit_code,
1555
+ signal=exit_signal,
1556
+ termination_reason=reason,
1557
+ run_id=run_id,
1558
+ final_tree_sha256=tree_sha256,
1559
+ descendants=descendants,
1560
+ proof=proof,
1561
+ )
1562
+ return returncode
1563
+
1564
+
1565
+ def main(argv: list[str] | None = None) -> int:
1566
+ parser = argparse.ArgumentParser(description=__doc__)
1567
+ parser.add_argument("--execution-id", required=True)
1568
+ parser.add_argument("--run-sh", required=True)
1569
+ parser.add_argument("--workspace", required=True)
1570
+ parser.add_argument("--loki-dir", required=True)
1571
+ parser.add_argument("--spec", required=True)
1572
+ parser.add_argument("--provider", required=True)
1573
+ parser.add_argument("--parallel", action="store_true")
1574
+ args = parser.parse_args(argv)
1575
+ result = run_supervisor(
1576
+ execution_id=args.execution_id,
1577
+ run_sh=Path(args.run_sh),
1578
+ workspace=Path(args.workspace),
1579
+ loki_dir=Path(args.loki_dir),
1580
+ spec=Path(args.spec),
1581
+ provider=args.provider,
1582
+ parallel=args.parallel,
1583
+ )
1584
+ if result < 0:
1585
+ return 128 + (-result)
1586
+ return min(result, 255)
1587
+
1588
+
1589
+ if __name__ == "__main__":
1590
+ raise SystemExit(main())
1591
+
1592
+
1593
+ __all__ = [
1594
+ "SCHEMA_VERSION",
1595
+ "confined_provider_auth_status",
1596
+ "empty_descendant_outcome",
1597
+ "empty_integrity_result",
1598
+ "empty_proof_binding",
1599
+ "execution_dir",
1600
+ "execution_lock",
1601
+ "executions_root",
1602
+ "host_confinement_identity",
1603
+ "open_private_log",
1604
+ "process_birth_token",
1605
+ "process_identity_matches",
1606
+ "process_state",
1607
+ "read_state",
1608
+ "read_state_unlocked",
1609
+ "run_supervisor",
1610
+ "sha256_bytes",
1611
+ "smoke_probe_confined_provider_auth",
1612
+ "smoke_probe_host_seatbelt",
1613
+ "state_path",
1614
+ "update_state",
1615
+ "utc_now",
1616
+ "validate_execution_id",
1617
+ "write_spec_snapshot_unlocked",
1618
+ "write_state_unlocked",
1619
+ ]