agent-bios 0.19.1 → 0.19.2

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 (85) hide show
  1. package/DEPENDENCIES.md +27 -27
  2. package/INSTALL.md +4 -4
  3. package/README.md +53 -26
  4. package/claude/CLAUDE.md +1 -1
  5. package/claude/guides/claude-prompting.md +1 -1
  6. package/claude/guides/cli-multi-model-workflow.md +3 -3
  7. package/claude/guides/documentation-hygiene.md +3 -0
  8. package/claude/guides/gpt-prompting.md +1 -1
  9. package/claude/guides/korean-writing.md +153 -0
  10. package/claude/guides/learning-flow.md +4 -4
  11. package/claude/guides/session-distill-workflow.md +8 -8
  12. package/claude/guides/slide-writing/RUNBOOK.md +5 -5
  13. package/claude/skills/repo-charter/SKILL.md +3 -3
  14. package/claude/skills/understand/SKILL.md +5 -5
  15. package/codex/AGENTS.md +1 -1
  16. package/codex/guides/claude-prompting.md +1 -1
  17. package/codex/guides/cli-multi-model-workflow.md +3 -3
  18. package/codex/guides/documentation-hygiene.md +3 -0
  19. package/codex/guides/gpt-prompting.md +1 -1
  20. package/codex/guides/korean-writing.md +153 -0
  21. package/codex/guides/learning-flow.md +4 -4
  22. package/codex/guides/session-distill-workflow.md +8 -8
  23. package/codex/guides/slide-writing/RUNBOOK.md +5 -5
  24. package/compose/app_bridge/SKILL.md +12 -12
  25. package/compose/app_bridge/scripts/bridge.py +15 -7
  26. package/compose/assemble.py +5 -5
  27. package/compose/bootstrap/SKILL.md +18 -18
  28. package/compose/canary.sh +4 -4
  29. package/compose/check-domains.py +6 -6
  30. package/compose/corpus-state.py +16 -1168
  31. package/compose/corpus.py +13 -402
  32. package/compose/corpus_app.py +14 -450
  33. package/compose/corpus_catalog.py +15 -926
  34. package/compose/corpus_import.py +14 -523
  35. package/compose/corpus_install.py +14 -1847
  36. package/compose/corpus_session.py +16 -848
  37. package/compose/corpus_setup.py +16 -672
  38. package/compose/corpus_setup_cli.py +15 -580
  39. package/compose/corpus_setup_i18n.py +20 -324
  40. package/compose/corpus_setup_ui.py +18 -645
  41. package/compose/corpus_store.py +16 -1664
  42. package/compose/corpus_transaction.py +15 -284
  43. package/compose/corpus_ui.py +17 -972
  44. package/compose/corpus_ui_runtime.py +16 -274
  45. package/compose/corpus_understand.py +13 -671
  46. package/compose/domains.json +2 -1
  47. package/compose/instructions-state.py +1175 -0
  48. package/compose/instructions.py +409 -0
  49. package/compose/instructions_app.py +464 -0
  50. package/compose/instructions_catalog.py +931 -0
  51. package/compose/instructions_import.py +529 -0
  52. package/compose/instructions_install.py +1866 -0
  53. package/compose/instructions_session.py +852 -0
  54. package/compose/instructions_setup.py +676 -0
  55. package/compose/instructions_setup_cli.py +586 -0
  56. package/compose/instructions_setup_i18n.py +324 -0
  57. package/compose/instructions_setup_ui.py +647 -0
  58. package/compose/instructions_store.py +1668 -0
  59. package/compose/instructions_transaction.py +306 -0
  60. package/compose/instructions_ui.py +975 -0
  61. package/compose/instructions_ui_runtime.py +278 -0
  62. package/compose/instructions_understand.py +678 -0
  63. package/compose/register-hooks.py +1 -1
  64. package/compose/setup/START.md +11 -11
  65. package/docs/advanced-launch.md +11 -11
  66. package/docs/instructions-compatibility.md +86 -0
  67. package/docs/{corpus.md → instructions.md} +35 -8
  68. package/docs/recovery.md +10 -10
  69. package/docs/releases/0.19.2.md +38 -0
  70. package/docs/session-model.md +23 -20
  71. package/docs/setup.md +27 -26
  72. package/docs/understand.md +6 -6
  73. package/install.sh +70 -69
  74. package/launch/agent-launch.py +298 -290
  75. package/launch/agent-launch.toml +2 -2
  76. package/launch/i18n/en.toml +55 -55
  77. package/launch/i18n/ja.toml +56 -56
  78. package/launch/i18n/ko.toml +56 -56
  79. package/launch/shell_integration.py +4 -4
  80. package/learn/collect-learning.py +10 -10
  81. package/learn/learning.schema.json +1 -1
  82. package/learn/migrate-learnings.py +51 -51
  83. package/package.json +27 -11
  84. package/provenance.json +1 -1
  85. /package/docs/assets/{corpus-studio.svg → instructions-studio.svg} +0 -0
@@ -1,278 +1,20 @@
1
- """Load the shipped pure-Python UI wheels offline into one process-owned directory."""
2
- from __future__ import annotations
1
+ #!/usr/bin/env python3
2
+ """Compatibility entrypoint for corpus_ui_runtime.py; canonical implementation: instructions_ui_runtime.py.
3
3
 
4
- import atexit
5
- from email.parser import BytesParser
6
- import hashlib
4
+ Retained for installed bridges, saved commands and third-party imports. Remove
5
+ only after the documented compatibility window and historical consumers close.
6
+ """
7
+ from pathlib import Path
7
8
  import importlib
8
- import io
9
- import json
10
- from pathlib import Path, PurePosixPath
11
- import re
12
- import stat
9
+ import runpy
13
10
  import sys
14
- import tempfile
15
- import threading
16
- from typing import Any
17
- import zipfile
18
- import zlib
19
11
 
20
-
21
- SCHEMA_VERSION = 1
22
- MAX_ARCHIVE_BYTES = 32 * 1024 * 1024
23
- MAX_EXPANDED_BYTES = 128 * 1024 * 1024
24
- _LOCK = threading.RLock()
25
- _ACTIVE: tuple[str, tempfile.TemporaryDirectory[str]] | None = None
26
-
27
-
28
- class UiRuntimeError(RuntimeError):
29
- pass
30
-
31
-
32
- def _canonical(value: Any) -> bytes:
33
- return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
34
-
35
-
36
- def _bundle_id(manifest: dict[str, Any]) -> str:
37
- return hashlib.sha256(_canonical({key: value for key, value in manifest.items() if key != "bundle_id"})).hexdigest()
38
-
39
-
40
- def _normalized_name(name: str) -> str:
41
- return re.sub(r"[-_.]+", "-", name).lower()
42
-
43
-
44
- def _members(archive: zipfile.ZipFile) -> list[zipfile.ZipInfo]:
45
- result = []
46
- seen = set()
47
- expanded = 0
48
- for info in archive.infolist():
49
- raw = info.filename.rstrip("/")
50
- path = PurePosixPath(raw)
51
- if not raw or path.is_absolute() or "\\" in raw or path.as_posix() != raw \
52
- or any(part in {".", ".."} for part in path.parts) \
53
- or any(ord(character) < 32 or ord(character) == 127 for character in raw):
54
- raise UiRuntimeError(f"unsafe UI wheel member: {info.filename!r}")
55
- if raw in seen:
56
- raise UiRuntimeError(f"duplicate UI wheel member: {raw}")
57
- seen.add(raw)
58
- mode = info.external_attr >> 16
59
- if stat.S_ISLNK(mode) or (stat.S_IFMT(mode) not in {0, stat.S_IFREG, stat.S_IFDIR}):
60
- raise UiRuntimeError(f"non-regular UI wheel member: {raw}")
61
- if info.is_dir():
62
- continue
63
- if info.flag_bits & 1:
64
- raise UiRuntimeError(f"encrypted UI wheel member: {raw}")
65
- if path.suffix.lower() in {".so", ".pyd", ".dll", ".dylib", ".pth", ".pyc"} \
66
- or path.parts[0].endswith(".data"):
67
- raise UiRuntimeError(f"UI wheel requires unsupported installation behavior: {raw}")
68
- expanded += info.file_size
69
- if expanded > MAX_EXPANDED_BYTES:
70
- raise UiRuntimeError("UI wheel exceeds the extraction size limit")
71
- result.append(info)
72
- if not result:
73
- raise UiRuntimeError("UI wheel has no files")
74
- return result
75
-
76
-
77
- def wheel_metadata(filename: str, data: bytes) -> dict[str, Any]:
78
- """Read wheel metadata and licensing without importing its Python code."""
79
- if Path(filename).name != filename or not re.fullmatch(r"[A-Za-z0-9_.!+-]+-py[0-9.]+-none-any\.whl", filename):
80
- raise UiRuntimeError(f"UI runtime requires a pure Python universal wheel: {filename}")
81
- if not data or len(data) > MAX_ARCHIVE_BYTES:
82
- raise UiRuntimeError(f"invalid UI wheel size: {filename}")
83
- try:
84
- with zipfile.ZipFile(io.BytesIO(data)) as archive:
85
- members = _members(archive)
86
- names = {member.filename for member in members}
87
- metadata_paths = [name for name in names if name.endswith(".dist-info/METADATA")]
88
- if len(metadata_paths) != 1:
89
- raise UiRuntimeError(f"UI wheel needs exactly one distribution metadata file: {filename}")
90
- metadata_path = metadata_paths[0]
91
- info_root = metadata_path.rsplit("/", 1)[0]
92
- wheel_path = info_root + "/WHEEL"
93
- if wheel_path not in names:
94
- raise UiRuntimeError(f"UI wheel lacks WHEEL metadata: {filename}")
95
- metadata = BytesParser().parsebytes(archive.read(metadata_path))
96
- wheel = BytesParser().parsebytes(archive.read(wheel_path))
97
- tags = wheel.get_all("Tag", [])
98
- if wheel.get("Root-Is-Purelib", "").lower() != "true" or not tags \
99
- or any(not re.fullmatch(r"py[0-9.]+-none-any", tag) for tag in tags):
100
- raise UiRuntimeError(f"UI wheel is not pure Python: {filename}")
101
- license_files = sorted(name for name in names if name.startswith(info_root + "/") and (
102
- "/licenses/" in name.lower() or PurePosixPath(name).name.lower().startswith(("license", "licence", "copying"))))
103
- if not license_files or any(not archive.read(name).strip() for name in license_files):
104
- raise UiRuntimeError(f"UI wheel lacks an upstream license: {filename}")
105
- modules = set()
106
- for name in names:
107
- parts = PurePosixPath(name).parts
108
- if parts[0].endswith(".dist-info"):
109
- continue
110
- root = parts[0][:-3] if len(parts) == 1 and parts[0].endswith(".py") else parts[0]
111
- if re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", root):
112
- modules.add(root)
113
- name, version = metadata.get("Name"), metadata.get("Version")
114
- if not isinstance(name, str) or not re.fullmatch(r"[A-Za-z0-9_.-]+", name) \
115
- or not isinstance(version, str) or not re.fullmatch(r"[A-Za-z0-9_.!+]+", version) or not modules:
116
- raise UiRuntimeError(f"invalid UI distribution metadata: {filename}")
117
- return {"name": _normalized_name(name), "version": version, "filename": filename,
118
- "sha256": hashlib.sha256(data).hexdigest(), "size_bytes": len(data),
119
- "expanded_bytes": sum(member.file_size for member in members),
120
- "modules": sorted(modules), "license_files": license_files,
121
- "requires_python": metadata.get("Requires-Python", ""),
122
- "requires_dist": metadata.get_all("Requires-Dist", [])}
123
- except (zipfile.BadZipFile, KeyError, OSError, UnicodeError, RuntimeError, ValueError, EOFError, zlib.error) as exc:
124
- if isinstance(exc, UiRuntimeError):
125
- raise
126
- raise UiRuntimeError(f"cannot read UI wheel {filename}: {exc}") from exc
127
-
128
-
129
- def _read_bundle(repo: Path) -> tuple[dict[str, Any], list[tuple[dict[str, Any], bytes]]]:
130
- bundle = Path(repo) / "compose/ui_runtime"
131
- manifest_path = bundle / "manifest.json"
132
- if bundle.is_symlink() or manifest_path.is_symlink() or not manifest_path.is_file():
133
- raise UiRuntimeError("bundled UI runtime manifest is missing or unsafe; reinstall the agent-bios package")
134
- try:
135
- manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
136
- except (OSError, ValueError) as exc:
137
- raise UiRuntimeError(f"cannot read UI runtime manifest: {exc}") from exc
138
- if not isinstance(manifest, dict) or manifest.get("schema_version") != SCHEMA_VERSION \
139
- or manifest.get("python_requirement") != ">=3.11" or manifest.get("source") != "pypi":
140
- raise UiRuntimeError("unsupported UI runtime manifest")
141
- if manifest.get("bundle_id") != _bundle_id(manifest):
142
- raise UiRuntimeError("UI runtime manifest fingerprint mismatch")
143
- requirements = manifest.get("root_requirement")
144
- rows = manifest.get("packages")
145
- if not isinstance(requirements, str) or not re.fullmatch(r"textual==[0-9][A-Za-z0-9_.!+]*", requirements) \
146
- or not isinstance(rows, list) or not rows:
147
- raise UiRuntimeError("UI runtime manifest has no valid package inventory")
148
- supplied = set()
149
- filenames = set()
150
- modules = set()
151
- files = set()
152
- expanded = 0
153
- archives = []
154
- for row in rows:
155
- if not isinstance(row, dict) or not isinstance(row.get("filename"), str):
156
- raise UiRuntimeError("UI runtime manifest has an invalid package row")
157
- filename = row["filename"]
158
- if Path(filename).name != filename or filename in filenames:
159
- raise UiRuntimeError(f"invalid or duplicate UI wheel name: {filename}")
160
- path = bundle / filename
161
- if path.is_symlink() or not path.is_file():
162
- raise UiRuntimeError(f"bundled UI wheel missing or unsafe: {filename}")
163
- if path.stat().st_size != row.get("size_bytes") or path.stat().st_size > MAX_ARCHIVE_BYTES:
164
- raise UiRuntimeError(f"UI wheel size mismatch: {filename}")
165
- data = path.read_bytes()
166
- if hashlib.sha256(data).hexdigest() != row.get("sha256"):
167
- raise UiRuntimeError(f"UI wheel SHA256 mismatch: {filename}")
168
- actual = wheel_metadata(filename, data)
169
- if actual != row:
170
- raise UiRuntimeError(f"UI wheel metadata differs from its manifest: {filename}")
171
- if row["name"] in supplied or modules.intersection(row["modules"]):
172
- raise UiRuntimeError(f"UI distributions overlap: {filename}")
173
- supplied.add(row["name"])
174
- filenames.add(filename)
175
- modules.update(row["modules"])
176
- with zipfile.ZipFile(io.BytesIO(data)) as archive:
177
- names = {member.filename for member in _members(archive)}
178
- if files.intersection(names):
179
- raise UiRuntimeError(f"UI wheel paths overlap: {filename}")
180
- files.update(names)
181
- expanded += row["expanded_bytes"]
182
- if expanded > MAX_EXPANDED_BYTES:
183
- raise UiRuntimeError("UI runtime exceeds the extraction size limit")
184
- archives.append((row, data))
185
- if {path.name for path in bundle.iterdir() if path.name != "manifest.json"} != filenames:
186
- raise UiRuntimeError("UI runtime directory differs from its exact wheel inventory")
187
- if not any(f"{row['name']}=={row['version']}" == requirements for row in rows):
188
- raise UiRuntimeError("UI runtime root Textual requirement is not present")
189
- return manifest, archives
190
-
191
-
192
- def runtime_inventory(repo: Path) -> dict[str, Any]:
193
- """Report bundled hashes, purity, and licenses without extraction or UI imports."""
194
- try:
195
- manifest, _archives = _read_bundle(repo)
196
- except (UiRuntimeError, OSError) as exc:
197
- return {"schema_version": SCHEMA_VERSION, "status": "unavailable", "issues": [str(exc)], "packages": []}
198
- return {**manifest, "status": "available", "issues": [],
199
- "package_count": len(manifest["packages"]),
200
- "size_bytes": sum(row["size_bytes"] for row in manifest["packages"]),
201
- "expanded_bytes": sum(row["expanded_bytes"] for row in manifest["packages"]),
202
- "license_file_count": sum(len(row["license_files"]) for row in manifest["packages"]),
203
- "pure_python": True}
204
-
205
-
206
- def _preloaded_modules(modules: set[str], active: Path | None) -> list[str]:
207
- conflicts = []
208
- for name, module in tuple(sys.modules.items()):
209
- if name.split(".", 1)[0] not in modules:
210
- continue
211
- filename = getattr(module, "__file__", None)
212
- if active is not None and isinstance(filename, str):
213
- try:
214
- Path(filename).resolve().relative_to(active)
215
- continue
216
- except (ValueError, OSError):
217
- pass
218
- conflicts.append(name)
219
- return sorted(conflicts)
220
-
221
-
222
- def _cleanup_runtime() -> None:
223
- global _ACTIVE
224
- if _ACTIVE is not None:
225
- _bundle, temporary = _ACTIVE
226
- sys.path[:] = [entry for entry in sys.path if entry != temporary.name]
227
- temporary.cleanup()
228
- _ACTIVE = None
229
-
230
-
231
- def release_ui_runtime() -> None:
232
- """Release owned files immediately before process handoff; UI reuse needs a fresh process."""
233
- with _LOCK:
234
- _cleanup_runtime()
235
-
236
-
237
- def activate_ui_runtime(repo: Path) -> Path:
238
- """Activate only verified shipped modules; imports must follow this call."""
239
- global _ACTIVE
240
- if sys.version_info < (3, 11):
241
- raise UiRuntimeError("the bundled UI runtime requires Python 3.11 or newer")
242
- with _LOCK:
243
- manifest, archives = _read_bundle(repo)
244
- if _ACTIVE is not None and _ACTIVE[0] != manifest["bundle_id"]:
245
- raise UiRuntimeError("another UI bundle is active; start a fresh process for this agent-bios package")
246
- active = Path(_ACTIVE[1].name).resolve() if _ACTIVE is not None else None
247
- names = {name for row, _data in archives for name in row["modules"]}
248
- conflicts = _preloaded_modules(names, active)
249
- if conflicts:
250
- raise UiRuntimeError("UI dependencies were imported before bundled activation: " + ", ".join(conflicts[:8])
251
- + "; start a fresh process and activate the bundle before importing UI modules")
252
- if active is not None:
253
- if not active.is_dir():
254
- raise UiRuntimeError("the active UI runtime directory is missing; start a fresh process")
255
- return active
256
- parent = Path(tempfile.gettempdir()).resolve()
257
- home = Path.home().resolve()
258
- if parent == home or home in parent.parents:
259
- parent = Path("/tmp").resolve()
260
- temporary = tempfile.TemporaryDirectory(prefix="agent-bios-ui-", dir=parent)
261
- target = Path(temporary.name)
262
- try:
263
- for _row, data in archives:
264
- with zipfile.ZipFile(io.BytesIO(data)) as archive:
265
- for member in _members(archive):
266
- path = target.joinpath(*PurePosixPath(member.filename).parts)
267
- path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
268
- with path.open("xb") as output:
269
- output.write(archive.read(member))
270
- path.chmod(0o600)
271
- sys.path.insert(0, str(target))
272
- importlib.invalidate_caches()
273
- _ACTIVE = (manifest["bundle_id"], temporary)
274
- atexit.register(_cleanup_runtime)
275
- return target
276
- except BaseException:
277
- temporary.cleanup()
278
- raise
12
+ if __name__ == "__main__":
13
+ runpy.run_path(str(Path(__file__).with_name('instructions_ui_runtime.py')), run_name="__main__")
14
+ else:
15
+ _name = 'instructions_ui_runtime'
16
+ _module = importlib.import_module("." + _name, __package__) if __package__ else importlib.import_module(_name)
17
+ for _symbol in tuple(vars(_module)):
18
+ if "Instructions" in _symbol:
19
+ setattr(_module, _symbol.replace("Instructions", "Corpus"), getattr(_module, _symbol))
20
+ sys.modules[__name__] = _module