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,278 @@
1
+ """Load the shipped pure-Python UI wheels offline into one process-owned directory."""
2
+ from __future__ import annotations
3
+
4
+ import atexit
5
+ from email.parser import BytesParser
6
+ import hashlib
7
+ import importlib
8
+ import io
9
+ import json
10
+ from pathlib import Path, PurePosixPath
11
+ import re
12
+ import stat
13
+ import sys
14
+ import tempfile
15
+ import threading
16
+ from typing import Any
17
+ import zipfile
18
+ import zlib
19
+
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
@@ -0,0 +1,147 @@
1
+ # Set up agent-bios through conversation
2
+
3
+ This guide uses the same installation controller as the terminal wizard. The
4
+ conversation collects choices and presents effects; `setup` validates and executes
5
+ them. It needs local command and file access on the intended machine, Bash, and
6
+ Python 3.11 or newer. Codex CLI, an additional model login, and Textual are not
7
+ requirements for this route.
8
+
9
+ ## Start without an installed skill
10
+
11
+ The user can request installation using the public repository link. Follow the
12
+ repository's root `INSTALL.md` to obtain and verify the source, select the language
13
+ and run `setup start`. The agent resolves paths; the user does not need a checkout,
14
+ an installed skill or a long installation prompt. An explicitly selected trusted
15
+ local source remains valid. Preserve the acquisition's exact revision and location
16
+ for the review and any resume. If the selected public revision lacks conversation
17
+ setup, report that limitation instead of invoking an older installation route.
18
+
19
+ ## Choose the entrypoint and language
20
+
21
+ For an already registered app bridge, run `python3 "$BRIDGE" setup start`, where
22
+ `BRIDGE` is the absolute helper path beside the loaded skill. Read the returned
23
+ `guide_path`. For an acquired source, use the verified `setup_argv` from its start
24
+ response. Keep using that entrypoint and the reported private roots throughout
25
+ the review. Shell variables from one app tool call do not persist to the next;
26
+ pass absolute arguments or set the variable in the same call.
27
+
28
+ `start` returns language choices, a suggested language and execution context without
29
+ probing dependencies or creating private setup state. Confirm English, 한국어 or
30
+ 日本語, honoring an explicit language already chosen by the user, before `inspect`.
31
+ Use the selected language for questions and explanations; retain identifiers,
32
+ paths, versions, raw diagnostics and command arguments exactly. Use a native
33
+ question control when the current host exposes one for this interaction, or ask
34
+ ordinary concise questions. The conversation does not require custom widgets.
35
+
36
+ The commands below use `agent-bios` as shorthand for that verified entrypoint. In
37
+ an app bridge call, replace `agent-bios setup` with `python3 "$BRIDGE" setup`.
38
+
39
+ ```bash
40
+ agent-bios setup inspect --language ko
41
+ agent-bios setup discover --project-root /absolute/project
42
+ ```
43
+
44
+ ## Collect and review the choices
45
+
46
+ Use `inspect`'s `default_plan`, inventory and choices. Present all dependency
47
+ capabilities with readiness, purpose and installation destination. Only returned
48
+ installation recipes are selectable; do not create shell recipes from model
49
+ memory. A dependency's presence does not authorize installing another one.
50
+
51
+ Collect the six plan fields without asking the user to author JSON:
52
+
53
+ - `selection_mode` and `targets`: keep saved policy, no active corpus, all available
54
+ corpus, or specific returned package/domain/item targets. Start from the returned
55
+ default. No active corpus retains private library assets but delivers no corpus.
56
+ - `dependencies`: chosen installable inventory IDs; an empty list installs none.
57
+ - `app_bridge`: explicit registration choice; registration enables discovery only.
58
+ - `project_roots` and `import_paths`: absolute project folders and explicitly
59
+ selected files from discovery. Capture is independent of corpus selection.
60
+
61
+ Keep saved policy uses `selection_mode: null` and `targets: null`. No active corpus
62
+ uses `"none"` and `[]`. Explicit choices use `"selected"` and their target list;
63
+ all available corpus uses `"selected"` and `["all"]`.
64
+
65
+ Discovery checks known global instruction locations and the specified project
66
+ roots. Show detected sources before selecting them. Capture preserves originals
67
+ and prepares private evidence for later model review; it is not an automatically
68
+ optimized personal corpus. Read the import procedure only when the user requests
69
+ that subsequent review.
70
+
71
+ Save choices to a new caller-owned artifact. Run `plan` and save its complete
72
+ stdout bytes to another new caller-owned artifact:
73
+
74
+ ```bash
75
+ agent-bios setup plan --language ko --input /absolute/choices.json > /absolute/review.json
76
+ ```
77
+
78
+ The returned review envelope contains `review_id`, `context`, `language`,
79
+ `preview` and `summary`. Keep the entire envelope; do not reconstruct it from the
80
+ summary, copy only `preview`, change its IDs, or accept truncated output. Show the
81
+ concrete private paths, selected dependency commands/destinations, corpus policy,
82
+ app discovery change and selected capture sources. Keep the exact artifact
83
+ available for inspection. Preparing these caller-owned files is separate from
84
+ applying installation effects.
85
+
86
+ Apply the saved envelope only when its concrete choices are authorized. Existing
87
+ authorization for those exact choices does not require another confirmation.
88
+
89
+ ```bash
90
+ agent-bios setup apply --input /absolute/review.json --review-id REVIEW_ID --yes
91
+ ```
92
+
93
+ The engine checks the reviewed context, source bytes, private state and package
94
+ before its effects. If the review is stale, show a fresh plan and explain the
95
+ changed effects; do not bypass checks. Report completed dependencies, private
96
+ installation, bridge registration and pending import separately. A partial failure
97
+ or cancellation does not roll back completed external package installations.
98
+
99
+ ## Continue or resume
100
+
101
+ ```bash
102
+ agent-bios setup status --review-id REVIEW_ID
103
+ agent-bios setup resume --review-id REVIEW_ID
104
+ ```
105
+
106
+ These commands do not execute the remaining installation. `status` reports the
107
+ recorded attempt in `receipt`, `result` and `state`; a past `complete` result does
108
+ not establish the current runtime's health. Inspect `handoff` for current readiness
109
+ and any `needs_action`. Package verification, full runtime verification and helper
110
+ verification are independent observations; one does not prove the others.
111
+ `replayed: true` means the existing attempt was returned without executing it again.
112
+ If `handoff.verification` is `"deferred"`, its readiness fields are null: they are
113
+ unverified, not false. Status can return recorded progress while setup holds the
114
+ private-state lock. Follow `needs_action` and retry status when that operation has
115
+ settled; require checked readiness before using a handoff to continue.
116
+
117
+ `resume` adds `remaining_plan`, `review` and `needs_action`. When `review` is
118
+ non-null, save that entire nested review object to a new caller-owned JSON artifact
119
+ using deterministic JSON extraction. Do not pass the enclosing status response or
120
+ `remaining_plan` to Apply. Preserve every review field, including `continuation`,
121
+ and use the nested review's own `review_id`. Review its remaining effects before
122
+ applying within the user's authorization. If `review` is null, inspect the stated
123
+ reason; unknown prior effects are not permission to replay package installation.
124
+ When `resumed_from` is present, the response has followed a receipt's `continued_by`
125
+ link to an existing continuation. Report the returned child `review_id` rather than
126
+ assuming the original ID is current.
127
+
128
+ After an explicit app registration succeeds, use `handoff.helper_argv` only when
129
+ `helper_usable` is true, appending `setup` and the desired operation.
130
+ `helper_verified` confirms helper bytes, while `helper_usable` also requires a
131
+ verified package and no pending work blocking it. Otherwise inspect `needs_action`
132
+ and current readiness before continuing. A usable helper can continue in this task
133
+ even if native skill discovery has not refreshed. Without a
134
+ verified helper, a verified runtime's `setup_argv` already includes `setup`;
135
+ `cli_argv` does not. Supply the returned `environment` with each call. These arrays
136
+ are command arguments, not shell snippets. Finish applying an existing review
137
+ through its reviewed entrypoint/context; changing entrypoints requires a fresh
138
+ review. Status and subsequent setup can use the verified handoff.
139
+ Do not create a duplicate personal skill or edit host discovery settings to force
140
+ refresh. Once discovered, `$agent-bios` is the ordinary entrypoint. Registration
141
+ on disk is not proof of discovery or corpus loading.
142
+
143
+ If capture completed, report its actual capture ID and returned next action. Its
144
+ semantic review, proposed consumption placement and revision-checked import are
145
+ separate from installation. Setup enables no hooks, native agent registration,
146
+ permissions or edits to global/project instruction files. Each app task requires
147
+ its own explicit corpus use; neither setup nor opening the app performs it.
@@ -0,0 +1,238 @@
1
+ {
2
+ "schema_version": 1,
3
+ "source": "pypi",
4
+ "python_requirement": ">=3.11",
5
+ "root_requirement": "textual==8.2.8",
6
+ "packages": [
7
+ {
8
+ "name": "linkify-it-py",
9
+ "version": "2.2.0",
10
+ "filename": "linkify_it_py-2.2.0-py3-none-any.whl",
11
+ "sha256": "3adc40eb5af300b2605fcfdb968c24e1d780a90f1f2221af7c15e5111e94d443",
12
+ "size_bytes": 21971,
13
+ "expanded_bytes": 66766,
14
+ "modules": [
15
+ "linkify_it"
16
+ ],
17
+ "license_files": [
18
+ "linkify_it_py-2.2.0.dist-info/licenses/LICENSE"
19
+ ],
20
+ "requires_python": ">=3.10",
21
+ "requires_dist": [
22
+ "pytest; extra == \"test\"",
23
+ "coverage; extra == \"test\"",
24
+ "pytest-cov; extra == \"test\"",
25
+ "pytest-timeout; extra == \"test\"",
26
+ "pre-commit; extra == \"dev\"",
27
+ "isort; extra == \"dev\"",
28
+ "flake8; extra == \"dev\"",
29
+ "black; extra == \"dev\"",
30
+ "pyproject-flake8; extra == \"dev\"",
31
+ "pytest; extra == \"benchmark\"",
32
+ "pytest-benchmark; extra == \"benchmark\"",
33
+ "sphinx; extra == \"doc\"",
34
+ "sphinx_book_theme; extra == \"doc\"",
35
+ "myst-parser; extra == \"doc\""
36
+ ]
37
+ },
38
+ {
39
+ "name": "markdown-it-py",
40
+ "version": "4.2.0",
41
+ "filename": "markdown_it_py-4.2.0-py3-none-any.whl",
42
+ "sha256": "9f7ebbcd14fe59494226453aed97c1070d83f8d24b6fc3a3bcf9a38092641c4a",
43
+ "size_bytes": 91687,
44
+ "expanded_bytes": 244684,
45
+ "modules": [
46
+ "markdown_it"
47
+ ],
48
+ "license_files": [
49
+ "markdown_it_py-4.2.0.dist-info/licenses/LICENSE",
50
+ "markdown_it_py-4.2.0.dist-info/licenses/LICENSE.markdown-it"
51
+ ],
52
+ "requires_python": ">=3.10",
53
+ "requires_dist": [
54
+ "mdurl~=0.1",
55
+ "psutil ; extra == \"benchmarking\"",
56
+ "pytest ; extra == \"benchmarking\"",
57
+ "pytest-benchmark ; extra == \"benchmarking\"",
58
+ "commonmark~=0.9 ; extra == \"compare\"",
59
+ "markdown~=3.4 ; extra == \"compare\"",
60
+ "mistletoe~=1.0 ; extra == \"compare\"",
61
+ "mistune~=3.0 ; extra == \"compare\"",
62
+ "panflute~=2.3 ; extra == \"compare\"",
63
+ "markdown-it-pyrs ; extra == \"compare\"",
64
+ "linkify-it-py>=1,<3 ; extra == \"linkify\"",
65
+ "mdit-py-plugins>=0.5.0 ; extra == \"plugins\"",
66
+ "gprof2dot ; extra == \"profiling\"",
67
+ "mdit-py-plugins>=0.5.0 ; extra == \"rtd\"",
68
+ "myst-parser ; extra == \"rtd\"",
69
+ "pyyaml ; extra == \"rtd\"",
70
+ "sphinx ; extra == \"rtd\"",
71
+ "sphinx-copybutton ; extra == \"rtd\"",
72
+ "sphinx-design ; extra == \"rtd\"",
73
+ "sphinx-book-theme~=1.0 ; extra == \"rtd\"",
74
+ "jupyter_sphinx ; extra == \"rtd\"",
75
+ "ipykernel ; extra == \"rtd\"",
76
+ "coverage ; extra == \"testing\"",
77
+ "pytest ; extra == \"testing\"",
78
+ "pytest-cov ; extra == \"testing\"",
79
+ "pytest-regressions ; extra == \"testing\"",
80
+ "pytest-timeout ; extra == \"testing\"",
81
+ "requests ; extra == \"testing\""
82
+ ]
83
+ },
84
+ {
85
+ "name": "mdit-py-plugins",
86
+ "version": "0.6.1",
87
+ "filename": "mdit_py_plugins-0.6.1-py3-none-any.whl",
88
+ "sha256": "214c82fb2ac524472ab6a5bcab1de80f73b50443e187f401bfd77efbc7c6481d",
89
+ "size_bytes": 66663,
90
+ "expanded_bytes": 172036,
91
+ "modules": [
92
+ "mdit_py_plugins"
93
+ ],
94
+ "license_files": [
95
+ "mdit_py_plugins-0.6.1.dist-info/licenses/LICENSE"
96
+ ],
97
+ "requires_python": ">=3.10",
98
+ "requires_dist": [
99
+ "markdown-it-py>=2.0.0,<5.0.0",
100
+ "pre-commit ; extra == \"code-style\"",
101
+ "myst-parser ; extra == \"rtd\"",
102
+ "sphinx-book-theme ; extra == \"rtd\"",
103
+ "coverage ; extra == \"testing\"",
104
+ "pytest ; extra == \"testing\"",
105
+ "pytest-cov ; extra == \"testing\"",
106
+ "pytest-regressions ; extra == \"testing\"",
107
+ "pytest-timeout ; extra == \"testing\""
108
+ ]
109
+ },
110
+ {
111
+ "name": "mdurl",
112
+ "version": "0.1.2",
113
+ "filename": "mdurl-0.1.2-py3-none-any.whl",
114
+ "sha256": "84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8",
115
+ "size_bytes": 9979,
116
+ "expanded_bytes": 23308,
117
+ "modules": [
118
+ "mdurl"
119
+ ],
120
+ "license_files": [
121
+ "mdurl-0.1.2.dist-info/LICENSE"
122
+ ],
123
+ "requires_python": ">=3.7",
124
+ "requires_dist": []
125
+ },
126
+ {
127
+ "name": "platformdirs",
128
+ "version": "4.11.8",
129
+ "filename": "platformdirs-4.11.8-py3-none-any.whl",
130
+ "sha256": "52f2f181bbfde907966932cc8312d967d02976422d66d537ea16092b8e291081",
131
+ "size_bytes": 24027,
132
+ "expanded_bytes": 129535,
133
+ "modules": [
134
+ "platformdirs"
135
+ ],
136
+ "license_files": [
137
+ "platformdirs-4.11.8.dist-info/licenses/LICENSE"
138
+ ],
139
+ "requires_python": ">=3.10",
140
+ "requires_dist": []
141
+ },
142
+ {
143
+ "name": "pygments",
144
+ "version": "2.21.0",
145
+ "filename": "pygments-2.21.0-py3-none-any.whl",
146
+ "sha256": "2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9",
147
+ "size_bytes": 1250147,
148
+ "expanded_bytes": 4621166,
149
+ "modules": [
150
+ "pygments"
151
+ ],
152
+ "license_files": [
153
+ "pygments-2.21.0.dist-info/licenses/AUTHORS",
154
+ "pygments-2.21.0.dist-info/licenses/LICENSE"
155
+ ],
156
+ "requires_python": ">=3.9",
157
+ "requires_dist": [
158
+ "colorama>=0.4.6; extra == 'windows-terminal'"
159
+ ]
160
+ },
161
+ {
162
+ "name": "rich",
163
+ "version": "15.0.0",
164
+ "filename": "rich-15.0.0-py3-none-any.whl",
165
+ "sha256": "33bd4ef74232fb73fe9279a257718407f169c09b78a87ad3d296f548e27de0bb",
166
+ "size_bytes": 310654,
167
+ "expanded_bytes": 1245816,
168
+ "modules": [
169
+ "rich"
170
+ ],
171
+ "license_files": [
172
+ "rich-15.0.0.dist-info/licenses/LICENSE"
173
+ ],
174
+ "requires_python": ">=3.9.0",
175
+ "requires_dist": [
176
+ "ipywidgets (>=7.5.1,<9) ; extra == \"jupyter\"",
177
+ "markdown-it-py (>=2.2.0)",
178
+ "pygments (>=2.13.0,<3.0.0)"
179
+ ]
180
+ },
181
+ {
182
+ "name": "textual",
183
+ "version": "8.2.8",
184
+ "filename": "textual-8.2.8-py3-none-any.whl",
185
+ "sha256": "267375fd402dc8d981457212efa71f0e3365fd17bba144ba9bb3ed7563cb374a",
186
+ "size_bytes": 731418,
187
+ "expanded_bytes": 2827661,
188
+ "modules": [
189
+ "textual"
190
+ ],
191
+ "license_files": [
192
+ "textual-8.2.8.dist-info/licenses/LICENSE"
193
+ ],
194
+ "requires_python": ">=3.9,<4.0",
195
+ "requires_dist": [
196
+ "markdown-it-py[linkify] (>=2.1.0)",
197
+ "mdit-py-plugins",
198
+ "platformdirs (>=3.6.0,<5)",
199
+ "pygments (>=2.19.2,<3.0.0)",
200
+ "rich (>=14.2.0)",
201
+ "tree-sitter (>=0.25.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
202
+ "tree-sitter-bash (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
203
+ "tree-sitter-css (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
204
+ "tree-sitter-go (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
205
+ "tree-sitter-html (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
206
+ "tree-sitter-java (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
207
+ "tree-sitter-javascript (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
208
+ "tree-sitter-json (>=0.24.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
209
+ "tree-sitter-markdown (>=0.3.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
210
+ "tree-sitter-python (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
211
+ "tree-sitter-regex (>=0.24.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
212
+ "tree-sitter-rust (>=0.23.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
213
+ "tree-sitter-sql (>=0.3.11) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
214
+ "tree-sitter-toml (>=0.6.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
215
+ "tree-sitter-xml (>=0.7.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
216
+ "tree-sitter-yaml (>=0.6.0) ; (python_version >= \"3.10\") and (extra == \"syntax\")",
217
+ "typing-extensions (>=4.4.0,<5.0.0)"
218
+ ]
219
+ },
220
+ {
221
+ "name": "typing-extensions",
222
+ "version": "4.16.0",
223
+ "filename": "typing_extensions-4.16.0-py3-none-any.whl",
224
+ "sha256": "481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8",
225
+ "size_bytes": 45571,
226
+ "expanded_bytes": 182767,
227
+ "modules": [
228
+ "typing_extensions"
229
+ ],
230
+ "license_files": [
231
+ "typing_extensions-4.16.0.dist-info/licenses/LICENSE"
232
+ ],
233
+ "requires_python": ">=3.9",
234
+ "requires_dist": []
235
+ }
236
+ ],
237
+ "bundle_id": "f86c64655f21c67f45a1b43cf5ea16da5f4b6957a5ce0b8b222e8e8f8d2c1194"
238
+ }