agent-bios 0.18.0 → 0.19.0

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 (53) hide show
  1. package/DEPENDENCIES.md +236 -80
  2. package/INSTALL.md +112 -0
  3. package/README.md +184 -524
  4. package/claude/CLAUDE.md +1 -1
  5. package/claude/guides/cli-multi-model-workflow.md +1 -1
  6. package/claude/guides/learning-flow.md +23 -12
  7. package/claude/guides/session-distill-workflow.md +22 -12
  8. package/codex/AGENTS.md +1 -1
  9. package/codex/guides/cli-multi-model-workflow.md +1 -1
  10. package/codex/guides/learning-flow.md +23 -12
  11. package/codex/guides/session-distill-workflow.md +22 -12
  12. package/compose/app_bridge/SKILL.md +75 -0
  13. package/compose/app_bridge/agents/openai.yaml +2 -0
  14. package/compose/app_bridge/scripts/bridge.py +76 -0
  15. package/compose/bootstrap/SKILL.md +12 -1
  16. package/compose/corpus.py +31 -9
  17. package/compose/corpus_app.py +456 -0
  18. package/compose/corpus_import.py +529 -0
  19. package/compose/corpus_install.py +196 -18
  20. package/compose/corpus_session.py +27 -0
  21. package/compose/corpus_setup.py +674 -0
  22. package/compose/corpus_setup_cli.py +582 -0
  23. package/compose/corpus_setup_i18n.py +318 -0
  24. package/compose/corpus_setup_ui.py +633 -0
  25. package/compose/corpus_store.py +167 -29
  26. package/compose/corpus_transaction.py +43 -10
  27. package/compose/corpus_ui_runtime.py +278 -0
  28. package/compose/setup/START.md +147 -0
  29. package/compose/ui_runtime/linkify_it_py-2.2.0-py3-none-any.whl +0 -0
  30. package/compose/ui_runtime/manifest.json +238 -0
  31. package/compose/ui_runtime/markdown_it_py-4.2.0-py3-none-any.whl +0 -0
  32. package/compose/ui_runtime/mdit_py_plugins-0.6.1-py3-none-any.whl +0 -0
  33. package/compose/ui_runtime/mdurl-0.1.2-py3-none-any.whl +0 -0
  34. package/compose/ui_runtime/platformdirs-4.11.8-py3-none-any.whl +0 -0
  35. package/compose/ui_runtime/pygments-2.21.0-py3-none-any.whl +0 -0
  36. package/compose/ui_runtime/rich-15.0.0-py3-none-any.whl +0 -0
  37. package/compose/ui_runtime/textual-8.2.8-py3-none-any.whl +0 -0
  38. package/compose/ui_runtime/typing_extensions-4.16.0-py3-none-any.whl +0 -0
  39. package/docs/advanced-launch.md +131 -0
  40. package/docs/assets/corpus-studio.svg +227 -0
  41. package/docs/corpus.md +117 -0
  42. package/docs/recovery.md +201 -0
  43. package/docs/session-model.md +120 -0
  44. package/docs/setup.md +190 -0
  45. package/docs/understand.md +40 -0
  46. package/install.sh +75 -46
  47. package/launch/agent-launch.py +91 -47
  48. package/launch/provision-venv.sh +44 -13
  49. package/learn/collect-learning.py +14 -5
  50. package/learn/learning.schema.json +2 -2
  51. package/package.json +14 -2
  52. package/provenance.json +1 -1
  53. package/wrappers/claude-run.sh +10 -13
@@ -0,0 +1,582 @@
1
+ """JSON setup adapter with read-only reviews and durable, non-replaying receipts."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import contextlib
6
+ import hashlib
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ import platform
11
+ import re
12
+ import shutil
13
+ import socket
14
+ import subprocess
15
+ import sys
16
+ import tempfile
17
+ import time
18
+ from typing import Any, Callable
19
+
20
+ if __name__ == "__main__":
21
+ sys.dont_write_bytecode = True
22
+
23
+ try:
24
+ from corpus_install import CorpusInstaller
25
+ from corpus_setup import SetupController, SetupError, format_setup_result, review_summary
26
+ from corpus_setup_i18n import LANGUAGES, choice_label, dependency_display, detect_language, translate
27
+ from corpus_transaction import confirmed_release, reject_symlink_ancestors, transaction_lock, try_transaction_lock
28
+ except ImportError:
29
+ from .corpus_install import CorpusInstaller
30
+ from .corpus_setup import SetupController, SetupError, format_setup_result, review_summary
31
+ from .corpus_setup_i18n import LANGUAGES, choice_label, dependency_display, detect_language, translate
32
+ from .corpus_transaction import confirmed_release, reject_symlink_ancestors, transaction_lock, try_transaction_lock
33
+
34
+
35
+ SCHEMA_VERSION = 1
36
+ IDENTIFIER = re.compile(r"[0-9a-f]{64}\Z")
37
+ LANGUAGE_IDS = {identifier for _label, identifier in LANGUAGES}
38
+ INCIDENTAL_ENV = {
39
+ **dict.fromkeys(("_", "SHLVL"), "shell command bookkeeping does not select setup effects"),
40
+ **dict.fromkeys(("PWD", "OLDPWD"), "the actual working directory is bound separately in the review context"),
41
+ **dict.fromkeys(("TERM", "COLORTERM", "TERM_PROGRAM", "TERM_PROGRAM_VERSION", "TERM_SESSION_ID", "SHELL_SESSION_ID",
42
+ "COLUMNS", "LINES", "NO_COLOR", "FORCE_COLOR"),
43
+ "terminal presentation and terminal-session identity are not installation authority"),
44
+ **dict.fromkeys(("LC_ALL", "LC_MESSAGES", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", "LANG", "LANGUAGE"),
45
+ "display locale may change between calls; the chosen review language is bound explicitly"),
46
+ **dict.fromkeys(("TMPDIR", "TMP", "TEMP"),
47
+ "scratch allocation may change between calls; package and private destinations are bound separately"),
48
+ **dict.fromkeys(("PYTHONDONTWRITEBYTECODE", "PYTHONUNBUFFERED"),
49
+ "Python cache and output policy do not select installation targets or recipes"),
50
+ **dict.fromkeys(("CODEX_THREAD_ID", "CODEX_TASK_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID"),
51
+ "setup does not activate a task and may continue from another task on the same machine and private roots"),
52
+ }
53
+
54
+
55
+ def _canonical(value: Any) -> bytes:
56
+ return json.dumps(value, sort_keys=True, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
57
+
58
+
59
+ def _digest(value: Any) -> str:
60
+ return hashlib.sha256(_canonical(value)).hexdigest()
61
+
62
+
63
+ def _identity() -> dict[str, Any]:
64
+ hardware = ""
65
+ method = "host-filesystem"
66
+ if sys.platform.startswith("linux"):
67
+ for path in (Path("/etc/machine-id"), Path("/var/lib/dbus/machine-id")):
68
+ try:
69
+ candidate = path.read_text(encoding="ascii").strip()
70
+ except (OSError, UnicodeError):
71
+ continue
72
+ if re.fullmatch(r"[a-fA-F0-9]{32}", candidate):
73
+ hardware, method = candidate, "machine-id"
74
+ break
75
+ elif sys.platform == "darwin":
76
+ try:
77
+ result = subprocess.run(["/usr/sbin/ioreg", "-rd1", "-c", "IOPlatformExpertDevice"],
78
+ stdin=subprocess.DEVNULL, capture_output=True, text=True, timeout=5)
79
+ match = re.search(r'"IOPlatformUUID"\s*=\s*"([A-Fa-f0-9-]+)"', result.stdout)
80
+ if result.returncode == 0 and match:
81
+ hardware, method = match[1], "platform-uuid"
82
+ except (OSError, subprocess.SubprocessError):
83
+ pass
84
+ host = socket.gethostname()
85
+ if not hardware:
86
+ root = Path("/").stat()
87
+ hardware = f"{host}:{root.st_dev}:{root.st_ino}"
88
+ return {"machine_id": hashlib.sha256(hardware.encode()).hexdigest(), "identity_method": method,
89
+ "hostname": host, "platform": platform.system(), "architecture": platform.machine(),
90
+ "uid": os.getuid() if hasattr(os, "getuid") else None}
91
+
92
+
93
+ def _process_identity(pid: int) -> str | None:
94
+ if sys.platform.startswith("linux"):
95
+ try:
96
+ fields = (Path("/proc") / str(pid) / "stat").read_text().rsplit(")", 1)[1].split()
97
+ return fields[19]
98
+ except (OSError, IndexError):
99
+ return None
100
+ try:
101
+ result = subprocess.run(["/bin/ps", "-p", str(pid), "-o", "lstart="], stdin=subprocess.DEVNULL,
102
+ capture_output=True, text=True, timeout=3,
103
+ env={**os.environ, "LC_ALL": "C", "TZ": "UTC"})
104
+ return result.stdout.strip() if result.returncode == 0 and result.stdout.strip() else None
105
+ except (OSError, subprocess.SubprocessError):
106
+ return None
107
+
108
+
109
+ def _read_json(path: Path) -> dict[str, Any]:
110
+ reject_symlink_ancestors(path)
111
+ if not path.is_file() or path.stat().st_size > 8 * 1024 * 1024:
112
+ raise SetupError(f"missing, unsafe, or oversized setup JSON: {path}")
113
+ try:
114
+ value = json.loads(path.read_text(encoding="utf-8"))
115
+ except (OSError, ValueError) as exc:
116
+ raise SetupError(f"cannot read setup JSON: {path}: {exc}") from exc
117
+ if not isinstance(value, dict):
118
+ raise SetupError("setup JSON must be an object")
119
+ return value
120
+
121
+
122
+ def _write_json(path: Path, value: dict[str, Any]) -> None:
123
+ reject_symlink_ancestors(path)
124
+ path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
125
+ descriptor, raw = tempfile.mkstemp(prefix=".setup-receipt-", dir=path.parent)
126
+ temporary = Path(raw)
127
+ try:
128
+ with os.fdopen(descriptor, "wb") as output:
129
+ output.write(_canonical(value) + b"\n")
130
+ output.flush()
131
+ os.fsync(output.fileno())
132
+ temporary.chmod(0o600)
133
+ os.replace(temporary, path)
134
+ directory = os.open(path.parent, os.O_RDONLY)
135
+ try:
136
+ os.fsync(directory)
137
+ finally:
138
+ os.close(directory)
139
+ finally:
140
+ temporary.unlink(missing_ok=True)
141
+
142
+
143
+ def _review_identity(review: dict[str, Any]) -> str:
144
+ return _digest({key: value for key, value in review.items() if key != "review_id"})
145
+
146
+
147
+ def _file_identity(path: Path) -> dict[str, Any]:
148
+ resolved = path.resolve()
149
+ if not resolved.is_file():
150
+ raise SetupError(f"reviewed dependency executable is unavailable: {path}")
151
+ digest = hashlib.sha256()
152
+ with resolved.open("rb") as source:
153
+ for chunk in iter(lambda: source.read(1024 * 1024), b""):
154
+ digest.update(chunk)
155
+ return {"path": str(resolved), "sha256": digest.hexdigest(), "size_bytes": resolved.stat().st_size}
156
+
157
+
158
+ class SetupService:
159
+ """Bind shared-engine reviews to a machine, then record every effect boundary."""
160
+
161
+ def __init__(self, installer: Any, *, controller_factory: Callable | None = None,
162
+ identity: Callable[[], dict[str, Any]] = _identity,
163
+ progress_hook: Callable[[dict[str, Any]], None] | None = None):
164
+ self.installer = installer
165
+ self.controller_factory = controller_factory or (lambda: SetupController(installer))
166
+ self.identity = identity
167
+ self.progress_hook = progress_hook
168
+
169
+ def context(self) -> dict[str, Any]:
170
+ env = self.installer.env
171
+ home = Path(env.get("HOME", str(Path.home()))).expanduser().resolve()
172
+ state = Path(getattr(self.installer, "state_root", env.get("AGENT_BIOS_STATE_DIR", home / ".local/share/agent-bios"))).expanduser().resolve()
173
+ user = Path(getattr(self.installer, "user_root", env.get("AGENT_BIOS_CORPUS_DIR", home / ".config/agent-bios/corpus"))).expanduser().resolve()
174
+ return {**self.identity(), "home": str(home), "state_dir": str(state), "user_dir": str(user),
175
+ "codex_dir": str(Path(env.get("CODEX_HOME", home / ".codex")).expanduser().resolve()),
176
+ "claude_dir": str(Path(env.get("CLAUDE_CONFIG_DIR", home / ".claude")).expanduser().resolve()),
177
+ "package_root": str(Path(self.installer.repo).resolve()), "cwd": str(Path.cwd().resolve()),
178
+ "python": {"executable": str(Path(sys.executable).resolve()), "version": platform.python_version()},
179
+ "environment_digest": _digest({key: value for key, value in env.items() if key not in INCIDENTAL_ENV})}
180
+
181
+ def _language(self, language: str | None) -> str:
182
+ value = language or detect_language(self.installer.env)
183
+ if value not in LANGUAGE_IDS:
184
+ raise SetupError("language must be en, ko, or ja")
185
+ return value
186
+
187
+ def _assert_owner(self, review: dict[str, Any]) -> None:
188
+ current = self.context()
189
+ if any(review["context"].get(key) != current.get(key) for key in ("machine_id", "uid", "home", "state_dir", "user_dir")):
190
+ raise SetupError("review receipt belongs to another machine or private root context")
191
+
192
+ def start(self, language: str | None = None) -> dict[str, Any]:
193
+ package = Path(self.installer.repo).resolve()
194
+ cli = ["/bin/bash", str(package / "install.sh")]
195
+ return {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-start",
196
+ "languages": [{"label": label, "id": identifier} for label, identifier in LANGUAGES],
197
+ "suggested_language": detect_language(self.installer.env), "language": self._language(language),
198
+ "context": self.context(), "guide_path": str(package / "compose/setup/START.md"),
199
+ "cli_argv": cli, "setup_argv": [*cli, "setup"]}
200
+
201
+ def inspect(self, language: str | None = None) -> dict[str, Any]:
202
+ language = self._language(language)
203
+ controller = self.controller_factory()
204
+ return {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-inspection", "context": self.context(),
205
+ "language": language, "dependencies": controller.dependencies, "choices": controller.choices,
206
+ "default_plan": controller.default_plan(),
207
+ "display": {"dependencies": [dependency_display(language, row) for row in controller.dependencies],
208
+ "choices": [{**row, "label": choice_label(language, row)} for row in controller.choices]}}
209
+
210
+ def discover(self, roots: list[str]) -> dict[str, Any]:
211
+ if not isinstance(roots, list) or not all(isinstance(root, str) and Path(root).is_absolute() and Path(root).is_dir() for root in roots):
212
+ raise SetupError("project roots must be existing absolute directories")
213
+ found = self.installer.setup_discover(roots)
214
+ return {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-discovery", "context": self.context(),
215
+ "sources": found["sources"], "omitted": found.get("omitted", [])}
216
+
217
+ def _actions(self, preview: dict[str, Any]) -> list[dict[str, Any]]:
218
+ identities = []
219
+ for action in preview["plan"]["dependency_actions"]:
220
+ command = action["argv"][0]
221
+ path = shutil.which(command, path=self.installer.env.get("PATH", os.defpath))
222
+ if not path:
223
+ raise SetupError(f"reviewed dependency executable is unavailable: {command}")
224
+ identities.append({"id": action["id"], **_file_identity(Path(path))})
225
+ return identities
226
+
227
+ def plan(self, value: dict[str, Any], language: str | None = None, *, continuation: dict[str, Any] | None = None) -> dict[str, Any]:
228
+ language = self._language(language)
229
+ controller = self.controller_factory()
230
+ if not isinstance(value, dict) or set(value) != controller.FIELDS:
231
+ raise SetupError("plan input must contain exactly the six setup choice fields; derived commands are not input")
232
+ preview = controller.preview(value)
233
+ review = {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-review", "context": self.context(),
234
+ "language": language, "preview": preview, "action_inputs": self._actions(preview),
235
+ "summary": review_summary(preview["plan"], controller.dependencies, controller.choices, language)}
236
+ if continuation is not None:
237
+ review["continuation"] = continuation
238
+ review["review_id"] = _review_identity(review)
239
+ return review
240
+
241
+ def _receipt_path(self, identifier: str) -> Path:
242
+ if not isinstance(identifier, str) or not IDENTIFIER.fullmatch(identifier):
243
+ raise SetupError("review id must be an engine-issued 64-character identifier")
244
+ return Path(self.context()["state_dir"]) / "setup/receipts" / f"{identifier}.json"
245
+
246
+ def _receipt(self, identifier: str) -> dict[str, Any] | None:
247
+ path = self._receipt_path(identifier)
248
+ reject_symlink_ancestors(path)
249
+ if not path.exists():
250
+ return None
251
+ receipt = _read_json(path)
252
+ fingerprint = _digest({key: value for key, value in receipt.items() if key != "receipt_revision"})
253
+ if receipt.get("schema_version") != SCHEMA_VERSION or receipt.get("review_id") != identifier \
254
+ or receipt.get("receipt_revision") != fingerprint or not isinstance(receipt.get("operations"), dict) \
255
+ or receipt.get("state") not in {"running", "complete", "partial", "unknown"}:
256
+ raise SetupError("setup receipt integrity check failed")
257
+ self._validate_envelope(receipt.get("review"), identifier)
258
+ return receipt
259
+
260
+ def _save(self, receipt: dict[str, Any]) -> None:
261
+ receipt["updated_at"] = time.time()
262
+ receipt["receipt_revision"] = _digest({key: value for key, value in receipt.items() if key != "receipt_revision"})
263
+ _write_json(self._receipt_path(receipt["review_id"]), receipt)
264
+
265
+ @staticmethod
266
+ def _validate_envelope(review: Any, expected: str) -> None:
267
+ fields = {"schema_version", "kind", "context", "language", "preview", "action_inputs", "summary", "review_id"}
268
+ if not isinstance(review, dict) or set(review) not in (fields, fields | {"continuation"}) \
269
+ or review.get("schema_version") != SCHEMA_VERSION or review.get("kind") != "agent-bios-setup-review":
270
+ raise SetupError("apply requires the entire engine-issued review envelope")
271
+ if not isinstance(review["context"], dict) or not isinstance(review["preview"], dict) \
272
+ or not isinstance(review["language"], str) or review["language"] not in LANGUAGE_IDS \
273
+ or not isinstance(review["summary"], str) or not isinstance(review["action_inputs"], list):
274
+ raise SetupError("review envelope fields have invalid types")
275
+ if not isinstance(expected, str) or not IDENTIFIER.fullmatch(expected) or review.get("review_id") != expected \
276
+ or _review_identity(review) != expected:
277
+ raise SetupError("review identity does not match the exact reviewed envelope")
278
+
279
+ def _current_review(self, review: dict[str, Any]) -> dict[str, Any]:
280
+ if "continuation" in review:
281
+ continuation = review["continuation"]
282
+ if not isinstance(continuation, dict) or set(continuation) != {"review_id", "receipt_revision", "reuse_installation"}:
283
+ raise SetupError("invalid setup continuation")
284
+ resumed = self.resume(continuation["review_id"], review["language"])
285
+ if resumed.get("review") is None:
286
+ raise SetupError("setup continuation requires inspection of prior effects")
287
+ return resumed["review"]
288
+ value = review.get("preview", {}).get("plan", {})
289
+ plan = {key: value[key] for key in SetupController.FIELDS if key in value}
290
+ return self.plan(plan, review["language"])
291
+
292
+ def handoff(self) -> dict[str, Any]:
293
+ context = self.context()
294
+ result = {"verification": "deferred", "runtime_verified": None, "package_verified": None, "package_root": None, "release_digest": None, "cli_argv": None, "setup_argv": None,
295
+ "guide_path": None, "helper_registered": None, "helper_verified": None, "helper_argv": None,
296
+ "helper_usable": None,
297
+ "native_discovery": "unverified", "task_activation": {"performed": False, "existing_tasks": "unchanged", "default": "not_requested"},
298
+ "environment": {"HOME": context["home"], "AGENT_BIOS_STATE_DIR": context["state_dir"],
299
+ "AGENT_BIOS_CORPUS_DIR": context["user_dir"], "CODEX_HOME": context["codex_dir"],
300
+ "CLAUDE_CONFIG_DIR": context["claude_dir"]}, "needs_action": []}
301
+ try:
302
+ with try_transaction_lock(Path(context["state_dir"])) as acquired:
303
+ if not acquired:
304
+ result["needs_action"].append("private state verification is deferred: another operation holds the lock or synchronization is unavailable; retry setup status")
305
+ return result
306
+ result.update(verification="checked", runtime_verified=False, package_verified=False,
307
+ helper_registered=False, helper_verified=False, helper_usable=False)
308
+ return self._verified_handoff(context, result)
309
+ except (OSError, RuntimeError, ValueError) as exc:
310
+ result["needs_action"].append(str(exc))
311
+ return result
312
+
313
+ def _verified_handoff(self, context: dict[str, Any], result: dict[str, Any]) -> dict[str, Any]:
314
+ record = Path(context["state_dir"]) / "runtime/private-install.json"
315
+ pending = False
316
+ if record.is_file():
317
+ try:
318
+ reject_symlink_ancestors(record)
319
+ release = confirmed_release(Path(context["state_dir"]))
320
+ result.update(package_verified=True, package_root=str(release), release_digest=release.name,
321
+ cli_argv=["/bin/bash", str(release / "install.sh")],
322
+ setup_argv=["/bin/bash", str(release / "install.sh"), "setup"], guide_path=str(release / "compose/setup/START.md"))
323
+ result["environment"].update(AGENT_BIOS_PACKAGE_ROOT=str(release), AGENT_BIOS_PRIVATE_CORPUS="1", AGENT_BIOS_LEGACY_INSTALL="0")
324
+ except (OSError, RuntimeError, ValueError) as exc:
325
+ result["needs_action"].append(str(exc))
326
+ try:
327
+ verification = self.installer.verify()
328
+ result["runtime_verified"] = verification.get("stored") is True
329
+ if not result["runtime_verified"]:
330
+ result["needs_action"].append("private runtime verification did not confirm installation")
331
+ except (OSError, RuntimeError, ValueError) as exc:
332
+ result["needs_action"].append(str(exc))
333
+ try:
334
+ pending = bool(self.installer.status().get("transactions"))
335
+ if pending:
336
+ result["needs_action"].append("private installation has a pending transaction")
337
+ except (OSError, RuntimeError, ValueError) as exc:
338
+ pending = True
339
+ result["needs_action"].append(str(exc))
340
+ try:
341
+ manager = self.installer._app_manager()
342
+ bridge = manager.managed_status()
343
+ result["helper_registered"] = bool(bridge.get("registered"))
344
+ result["needs_action"].extend(bridge.get("needs_action", []))
345
+ if bridge.get("registered") and not bridge.get("needs_action"):
346
+ helper = manager.target / "scripts/bridge.py"
347
+ result.update(helper_verified=True, helper_argv=[sys.executable, str(helper)])
348
+ if "AGENT_LAUNCH_VENV" in self.installer.env:
349
+ result["environment"]["AGENT_LAUNCH_VENV"] = self.installer.env["AGENT_LAUNCH_VENV"]
350
+ except (OSError, RuntimeError, ValueError) as exc:
351
+ result["needs_action"].append(str(exc))
352
+ result["helper_usable"] = result["helper_verified"] and result["package_verified"] and not pending
353
+ return result
354
+
355
+ def status(self, identifier: str, language: str | None = None, *, replayed: bool = False) -> dict[str, Any]:
356
+ receipt = self._receipt(identifier)
357
+ result = receipt.get("result") or {} if receipt else {}
358
+ language = self._language(language or (receipt or {}).get("review", {}).get("language"))
359
+ state = receipt["state"] if receipt else "missing"
360
+ if state == "running":
361
+ owner = receipt.get("owner", {})
362
+ token = _process_identity(owner.get("pid", -1)) if isinstance(owner.get("pid"), int) else None
363
+ same_machine = receipt["review"]["context"].get("machine_id") == self.context()["machine_id"]
364
+ if not same_machine or token is None or token != owner.get("process_identity"):
365
+ state = "unknown"
366
+ return {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-status", "review_id": identifier,
367
+ "state": state, "receipt_path": str(self._receipt_path(identifier)), "receipt": receipt,
368
+ "result": result, "summary": format_setup_result(result, language, interface="conversation"), "handoff": self.handoff(),
369
+ "replayed": replayed, "context": self.context(), "language": language}
370
+
371
+ def resume(self, identifier: str, language: str | None = None) -> dict[str, Any]:
372
+ response = self.status(identifier, language)
373
+ response.update(remaining_plan=None, review=None, needs_action=[])
374
+ receipt = response["receipt"]
375
+ if receipt is None:
376
+ response["needs_action"] = ["setup receipt was not found"]
377
+ return response
378
+ self._assert_owner(receipt["review"])
379
+ chain = []
380
+ while receipt.get("continued_by"):
381
+ if receipt["review_id"] in chain or len(chain) >= 32:
382
+ raise SetupError("setup continuation chain is invalid")
383
+ chain.append(receipt["review_id"])
384
+ child = self._receipt(receipt["continued_by"])
385
+ if child is None or child["review"].get("continuation", {}).get("review_id") != receipt["review_id"]:
386
+ raise SetupError("setup continuation receipt is missing or inconsistent")
387
+ self._assert_owner(child["review"])
388
+ receipt = child
389
+ response = self.status(receipt["review_id"], language)
390
+ response.update(remaining_plan=None, review=None, needs_action=[], resumed_from=identifier)
391
+ if receipt["state"] == "complete":
392
+ return response
393
+ if receipt["state"] == "running":
394
+ response["needs_action"] = ["setup is running or its interruption is unconfirmed; inspect its recorded progress before retrying"]
395
+ return response
396
+ unsafe = [name for name, operation in receipt.get("operations", {}).items()
397
+ if operation.get("state") in {"running", "failed", "unknown"}]
398
+ if unsafe or receipt["state"] == "unknown":
399
+ response["needs_action"] = ["inspect effects before retrying: " + ", ".join(unsafe or ["unknown operation"])]
400
+ response["inspection"] = self.inspect(response["language"])
401
+ return response
402
+ original = receipt["review"]["preview"]["plan"]
403
+ plan = {key: original[key] for key in SetupController.FIELDS}
404
+ completed = {name for name, operation in receipt.get("operations", {}).items() if operation.get("state") == "completed"}
405
+ controller = self.controller_factory()
406
+ inventory = {row["id"]: row for row in controller.dependencies}
407
+ remaining = []
408
+ for name in plan["dependencies"]:
409
+ row = inventory.get(name)
410
+ if row is None:
411
+ response["needs_action"].append(f"requested dependency is absent from the current inventory: {name}")
412
+ elif "dependency:" + name in completed:
413
+ if row.get("status") != "available":
414
+ response["needs_action"].append(f"completed dependency is not currently available: {name}")
415
+ elif row.get("status") == "available":
416
+ continue
417
+ elif not row.get("install_argv"):
418
+ response["needs_action"].append(f"requested dependency is still missing and has no current installation recipe: {name}")
419
+ else:
420
+ remaining.append(name)
421
+ response["inspection"] = {"dependencies": controller.dependencies, "choices": controller.choices}
422
+ if response["needs_action"]:
423
+ return response
424
+ plan["dependencies"] = remaining
425
+ reuse = "install" in completed
426
+ if reuse:
427
+ if not response["handoff"]["runtime_verified"] or response["handoff"]["needs_action"]:
428
+ response["needs_action"] = ["verify the completed private installation before continuing"]
429
+ return response
430
+ plan["selection_mode"], plan["targets"] = None, None
431
+ if "extras" in completed:
432
+ plan["app_bridge"], plan["import_paths"], plan["project_roots"] = False, [], []
433
+ continuation = {"review_id": receipt["review_id"], "receipt_revision": receipt["receipt_revision"], "reuse_installation": reuse}
434
+ response["remaining_plan"] = plan
435
+ candidate = self.plan(plan, response["language"], continuation=continuation)
436
+ if reuse:
437
+ completed_install = receipt["operations"]["install"]["event"].get("installation", {})
438
+ completed_digest = completed_install.get("record", {}).get("release_digest") or completed_install.get("release_digest")
439
+ source_digest = candidate["preview"]["installation"].get("release_digest")
440
+ if source_digest != response["handoff"].get("release_digest") or completed_digest != source_digest:
441
+ response["needs_action"] = ["source or installed package changed after partial setup; create a fresh installation plan"]
442
+ return response
443
+ response["review"] = candidate
444
+ return response
445
+
446
+ def apply(self, review: dict[str, Any], expected_review_id: str, *, yes: bool = False) -> dict[str, Any]:
447
+ if yes is not True:
448
+ raise SetupError("apply requires explicit --yes acknowledgment of the exact reviewed plan")
449
+ review = json.loads(json.dumps(review, ensure_ascii=False))
450
+ self._validate_envelope(review, expected_review_id)
451
+ existing = self._receipt(expected_review_id)
452
+ if existing is not None:
453
+ if existing["review"] != review:
454
+ raise SetupError("review differs from the owned receipt")
455
+ self._assert_owner(review)
456
+ return self.status(expected_review_id, replayed=True)
457
+ if self._current_review(review) != review:
458
+ raise SetupError("review is stale or belongs to another execution context; create a fresh plan")
459
+ state_root = Path(self.context()["state_dir"])
460
+ with transaction_lock(state_root):
461
+ if self._receipt(expected_review_id) is not None:
462
+ return self.status(expected_review_id, replayed=True)
463
+ if self._current_review(review) != review:
464
+ raise SetupError("review changed while waiting for the setup lock; create a fresh plan")
465
+ receipt = {"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-receipt", "review_id": expected_review_id,
466
+ "review": review, "state": "running", "created_at": time.time(), "operations": {}, "progress": [], "result": None,
467
+ "owner": {"pid": os.getpid(), "process_identity": _process_identity(os.getpid())}}
468
+ self._save(receipt)
469
+ if review.get("continuation"):
470
+ parent = self._receipt(review["continuation"]["review_id"])
471
+ if parent is None or parent["receipt_revision"] != review["continuation"]["receipt_revision"] or parent.get("continued_by"):
472
+ raise SetupError("setup continuation was consumed or changed before execution")
473
+ parent["continued_by"] = expected_review_id
474
+ self._save(parent)
475
+ def progress(event: dict[str, Any]) -> None:
476
+ stage, phase = event.get("stage"), event.get("phase")
477
+ key = "dependency:" + str(event.get("dependency", event.get("id", ""))) if stage == "dependency" else stage
478
+ if phase == "started" and stage == "dependency":
479
+ actual = self._actions({"plan": {"dependency_actions": [action for action in review["preview"]["plan"]["dependency_actions"] if action["id"] == event["dependency"]]}})
480
+ expected = [row for row in review["action_inputs"] if row["id"] == event["dependency"]]
481
+ if actual != expected:
482
+ raise SetupError("dependency executable changed after review")
483
+ if stage in {"dependency", "install", "extras"}:
484
+ receipt["operations"][key] = {"state": {"started": "running", "completed": "completed", "failed": "failed", "not_started": "not_started"}.get(phase, "not_started"),
485
+ "event": event, "at": time.time()}
486
+ receipt["progress"].append(event)
487
+ self._save(receipt)
488
+ if self.progress_hook:
489
+ self.progress_hook(event)
490
+ try:
491
+ controller = self.controller_factory()
492
+ plan = {key: review["preview"]["plan"][key] for key in SetupController.FIELDS}
493
+ result = controller.apply(plan, preview=review["preview"], progress=progress,
494
+ reuse_installation=bool(review.get("continuation", {}).get("reuse_installation")))
495
+ receipt["result"] = result
496
+ active = any(operation["state"] == "running" for operation in receipt["operations"].values())
497
+ receipt["state"] = "complete" if result.get("applied") else "unknown" if active else "partial"
498
+ except BaseException as exc:
499
+ active = any(operation["state"] == "running" for operation in receipt["operations"].values())
500
+ receipt["state"] = "unknown" if active else "partial"
501
+ receipt["error"] = {"type": type(exc).__name__, "message": str(exc)}
502
+ receipt["result"] = {"applied": False, "installation_error": str(exc) or type(exc).__name__,
503
+ "dependency_results": [operation["event"] for key, operation in receipt["operations"].items() if key.startswith("dependency:") and "returncode" in operation["event"]]}
504
+ self._save(receipt)
505
+ return self.status(expected_review_id)
506
+
507
+
508
+ class _Parser(argparse.ArgumentParser):
509
+ def error(self, message):
510
+ raise SetupError(message)
511
+
512
+
513
+ def _input(source: str) -> dict[str, Any]:
514
+ if source != "-":
515
+ return _read_json(Path(source).expanduser())
516
+ if sys.stdin.isatty():
517
+ raise SetupError("--input - requires piped JSON; use a review file in a terminal")
518
+ body = sys.stdin.read(8 * 1024 * 1024 + 1)
519
+ if len(body) > 8 * 1024 * 1024:
520
+ raise SetupError("setup input is too large")
521
+ value = json.loads(body)
522
+ if not isinstance(value, dict):
523
+ raise SetupError("setup input must be a JSON object")
524
+ return value
525
+
526
+
527
+ def main(argv: list[str] | None = None) -> int:
528
+ args = None
529
+ presentation_language = None
530
+ try:
531
+ parser = _Parser(prog="agent-bios setup", add_help=False)
532
+ parser.add_argument("--repo", type=Path, default=Path(__file__).resolve().parents[1])
533
+ parser.add_argument("--help", action="store_true")
534
+ sub = parser.add_subparsers(dest="command")
535
+ for name in ("start", "inspect"):
536
+ command = sub.add_parser(name, add_help=False)
537
+ command.add_argument("--language", choices=sorted(LANGUAGE_IDS))
538
+ discover = sub.add_parser("discover", add_help=False)
539
+ discover.add_argument("--project-root", action="append", default=[])
540
+ plan = sub.add_parser("plan", add_help=False)
541
+ plan.add_argument("--input", required=True)
542
+ plan.add_argument("--language", choices=sorted(LANGUAGE_IDS))
543
+ apply = sub.add_parser("apply", add_help=False)
544
+ apply.add_argument("--input", required=True)
545
+ apply.add_argument("--review-id", required=True)
546
+ apply.add_argument("--yes", action="store_true")
547
+ for name in ("status", "resume"):
548
+ command = sub.add_parser(name, add_help=False)
549
+ command.add_argument("--review-id", required=True)
550
+ command.add_argument("--language", choices=sorted(LANGUAGE_IDS))
551
+ raw = list(sys.argv[1:] if argv is None else argv)
552
+ args = parser.parse_args([value for value in raw if value != "--json"])
553
+ if args.help:
554
+ result = {"schema_version": SCHEMA_VERSION, "commands": ["start", "inspect", "discover", "plan", "apply", "status", "resume"], "format": "JSON"}
555
+ else:
556
+ service = SetupService(CorpusInstaller(args.repo))
557
+ command = args.command or "start"
558
+ with contextlib.redirect_stdout(sys.stderr):
559
+ if command == "start": result = service.start(getattr(args, "language", None))
560
+ elif command == "inspect": result = service.inspect(args.language)
561
+ elif command == "discover": result = service.discover(args.project_root)
562
+ elif command == "plan": result = service.plan(_input(args.input), args.language)
563
+ elif command == "apply":
564
+ envelope = _input(args.input)
565
+ selected_language = envelope.get("language")
566
+ if isinstance(selected_language, str) and selected_language in LANGUAGE_IDS:
567
+ presentation_language = selected_language
568
+ result = service.apply(envelope, args.review_id, yes=args.yes)
569
+ elif command == "status": result = service.status(args.review_id, args.language)
570
+ else: result = service.resume(args.review_id, args.language)
571
+ print(json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True))
572
+ return 1 if (args.command == "apply" and result.get("state") != "complete") else 0
573
+ except (OSError, RuntimeError, ValueError, TypeError, KeyError) as exc:
574
+ language = presentation_language or getattr(args, "language", None) or detect_language(os.environ)
575
+ print(json.dumps({"schema_version": SCHEMA_VERSION, "kind": "agent-bios-setup-error", "ok": False,
576
+ "error": {"type": type(exc).__name__, "message": str(exc)},
577
+ "summary": translate(language, "Setup could not finish. Details: {detail}", detail=str(exc))}, ensure_ascii=False))
578
+ return 2
579
+
580
+
581
+ if __name__ == "__main__":
582
+ raise SystemExit(main())