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.
- package/DEPENDENCIES.md +236 -80
- package/INSTALL.md +112 -0
- package/README.md +184 -524
- package/claude/CLAUDE.md +1 -1
- package/claude/guides/cli-multi-model-workflow.md +1 -1
- package/claude/guides/learning-flow.md +23 -12
- package/claude/guides/session-distill-workflow.md +22 -12
- package/codex/AGENTS.md +1 -1
- package/codex/guides/cli-multi-model-workflow.md +1 -1
- package/codex/guides/learning-flow.md +23 -12
- package/codex/guides/session-distill-workflow.md +22 -12
- package/compose/app_bridge/SKILL.md +75 -0
- package/compose/app_bridge/agents/openai.yaml +2 -0
- package/compose/app_bridge/scripts/bridge.py +76 -0
- package/compose/bootstrap/SKILL.md +12 -1
- package/compose/corpus.py +31 -9
- package/compose/corpus_app.py +456 -0
- package/compose/corpus_import.py +529 -0
- package/compose/corpus_install.py +196 -18
- package/compose/corpus_session.py +27 -0
- package/compose/corpus_setup.py +674 -0
- package/compose/corpus_setup_cli.py +582 -0
- package/compose/corpus_setup_i18n.py +318 -0
- package/compose/corpus_setup_ui.py +633 -0
- package/compose/corpus_store.py +167 -29
- package/compose/corpus_transaction.py +43 -10
- package/compose/corpus_ui_runtime.py +278 -0
- package/compose/setup/START.md +147 -0
- package/compose/ui_runtime/linkify_it_py-2.2.0-py3-none-any.whl +0 -0
- package/compose/ui_runtime/manifest.json +238 -0
- package/compose/ui_runtime/markdown_it_py-4.2.0-py3-none-any.whl +0 -0
- package/compose/ui_runtime/mdit_py_plugins-0.6.1-py3-none-any.whl +0 -0
- package/compose/ui_runtime/mdurl-0.1.2-py3-none-any.whl +0 -0
- package/compose/ui_runtime/platformdirs-4.11.8-py3-none-any.whl +0 -0
- package/compose/ui_runtime/pygments-2.21.0-py3-none-any.whl +0 -0
- package/compose/ui_runtime/rich-15.0.0-py3-none-any.whl +0 -0
- package/compose/ui_runtime/textual-8.2.8-py3-none-any.whl +0 -0
- package/compose/ui_runtime/typing_extensions-4.16.0-py3-none-any.whl +0 -0
- package/docs/advanced-launch.md +131 -0
- package/docs/assets/corpus-studio.svg +227 -0
- package/docs/corpus.md +117 -0
- package/docs/recovery.md +201 -0
- package/docs/session-model.md +120 -0
- package/docs/setup.md +190 -0
- package/docs/understand.md +40 -0
- package/install.sh +75 -46
- package/launch/agent-launch.py +91 -47
- package/launch/provision-venv.sh +44 -13
- package/learn/collect-learning.py +14 -5
- package/learn/learning.schema.json +2 -2
- package/package.json +14 -2
- package/provenance.json +1 -1
- package/wrappers/claude-run.sh +10 -13
package/install.sh
CHANGED
|
@@ -1,32 +1,19 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# agent-bios installer.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
# overwrite, and reversible. Distributed as an npm bin; the actual $HOME
|
|
8
|
-
# deployment is this explicit command (never a postinstall side effect).
|
|
4
|
+
# Stores the corpus and runtime privately for explicitly activated sessions.
|
|
5
|
+
# Native global instructions and host discovery paths remain user-owned.
|
|
6
|
+
# AGENT_BIOS_LEGACY_INSTALL=1 selects the compatibility deployer.
|
|
9
7
|
#
|
|
10
|
-
# Usage:
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
# agent-bios status show what is installed and where
|
|
14
|
-
# agent-bios cost the session cost / context meter, from any directory
|
|
15
|
-
# agent-bios update git pull + reinstall (clone), or print the npm update line
|
|
16
|
-
# agent-bios update --check cache the registry's latest version for the TUI badge
|
|
17
|
-
# agent-bios uninstall remove deployed files and the zsh hook
|
|
18
|
-
# agent-bios help
|
|
19
|
-
#
|
|
20
|
-
# Flags: --dry-run (print actions, change nothing).
|
|
21
|
-
# Env overrides: CLAUDE_CONFIG_DIR, CODEX_HOME, AGENT_LAUNCH_VENV, ZDOTDIR.
|
|
8
|
+
# Usage: agent-bios help
|
|
9
|
+
# Private roots: AGENT_BIOS_STATE_DIR, AGENT_BIOS_CORPUS_DIR.
|
|
10
|
+
# Host/runtime overrides: CLAUDE_CONFIG_DIR, CODEX_HOME, AGENT_LAUNCH_VENV, ZDOTDIR.
|
|
22
11
|
set -euo pipefail
|
|
23
12
|
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# `learn` is the one subcommand whose payload IS stdin, so keep the caller's on
|
|
29
|
-
# fd 3 first and hand it back only there; every other path still sees /dev/null.
|
|
13
|
+
# Preserve caller input for explicit interactive setup and payload commands;
|
|
14
|
+
# dependency and compatibility child processes receive /dev/null by default.
|
|
15
|
+
# Preserve caller input on fd 3 for payload-bearing subcommands (learn, corpus,
|
|
16
|
+
# understand, and cost); other child processes see /dev/null.
|
|
30
17
|
# The braces matter. `exec` with redirections and no command applies them to the
|
|
31
18
|
# SHELL, permanently — so the bare `exec 3<&0 2>/dev/null` this used to be sent
|
|
32
19
|
# every later error message on this script's stderr to /dev/null: bash's own
|
|
@@ -1745,7 +1732,7 @@ cmd_update() {
|
|
|
1745
1732
|
if [ "${AGENT_BIOS_LEGACY_INSTALL:-0}" = 1 ]; then
|
|
1746
1733
|
cmd_install
|
|
1747
1734
|
else
|
|
1748
|
-
python3 "$REPO/compose/corpus_install.py" --repo "$REPO" install
|
|
1735
|
+
python3 "$REPO/compose/corpus_install.py" --repo "$REPO" install --non-interactive
|
|
1749
1736
|
fi
|
|
1750
1737
|
else
|
|
1751
1738
|
log "Installed as an npm package. Update with:"
|
|
@@ -1760,9 +1747,22 @@ usage() {
|
|
|
1760
1747
|
cat <<'EOF'
|
|
1761
1748
|
agent-bios — manage private corpus content for explicitly activated sessions.
|
|
1762
1749
|
|
|
1763
|
-
agent-bios install
|
|
1750
|
+
agent-bios install open the Textual installation wizard
|
|
1751
|
+
agent-bios install --non-interactive --corpus none store runtime with no active corpus
|
|
1752
|
+
agent-bios install --corpus selected --select @scope/package select only that corpus
|
|
1764
1753
|
agent-bios onboard select domains for future activated sessions
|
|
1754
|
+
agent-bios setup start report setup languages, execution target, and conversation guide
|
|
1755
|
+
agent-bios setup inspect --language ko inspect dependencies and corpus choices as JSON
|
|
1756
|
+
agent-bios setup discover --project-root /path/to/project list eligible instruction sources
|
|
1757
|
+
agent-bios setup plan --language ko --input request.json preview selected setup effects as JSON
|
|
1758
|
+
agent-bios setup apply --input review.json --review-id ID --yes apply the accepted review
|
|
1759
|
+
agent-bios setup status --review-id ID inspect a recorded setup outcome
|
|
1760
|
+
agent-bios setup resume --review-id ID re-probe and prepare a fresh review without applying
|
|
1765
1761
|
agent-bios corpus open Corpus Studio; list/show/plan/apply also work non-TTY
|
|
1762
|
+
agent-bios import discover/capture/review local instructions; --help lists source-preserving steps
|
|
1763
|
+
agent-bios app register an explicit Codex app bridge or manage per-task corpus input
|
|
1764
|
+
agent-bios app session use return selected corpus as context for this Codex app task
|
|
1765
|
+
agent-bios app session off stop future delivery; earlier context requires a new task to exclude
|
|
1766
1766
|
agent-bios understand list corpus learning bundles; --help shows session/discovery commands
|
|
1767
1767
|
agent-bios shell show the optional zsh connection status
|
|
1768
1768
|
agent-bios shell restore make bare claude/codex open the launcher TUI
|
|
@@ -1795,18 +1795,21 @@ AGENTS.md/CLAUDE.md. Restore/remove it from the TUI's Shell connection menu or
|
|
|
1795
1795
|
the commands above. After restoring, open a new terminal or reload .zshrc.
|
|
1796
1796
|
|
|
1797
1797
|
Flags: --dry-run print actions without changing anything
|
|
1798
|
-
--
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
--
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1798
|
+
--interactive open the Textual installation wizard (default)
|
|
1799
|
+
--non-interactive opt out of the wizard and return JSON; required without a terminal
|
|
1800
|
+
--corpus / --select seed wizard choices; --dry-run previews without Apply
|
|
1801
|
+
--corpus none deliver no corpus, including core and management bootstrap
|
|
1802
|
+
--corpus all select all corpus; --corpus selected uses repeated --select targets
|
|
1803
|
+
Library assets remain in the private runtime; these choices govern delivery.
|
|
1804
|
+
--domains a,b select named domains for future activated snapshots, plus
|
|
1805
|
+
implicit core+infra; '--domains none' keeps that core-only meaning. Saved selections
|
|
1806
|
+
survive installs/updates. Without a saved selection or flag,
|
|
1807
|
+
installation selects every domain.
|
|
1808
|
+
--with a,b optional dependency installation in explicit legacy mode only;
|
|
1809
|
+
private installation rejects this option.
|
|
1810
|
+
Env: AGENT_BIOS_STATE_DIR, AGENT_BIOS_CORPUS_DIR (private runtime and user roots)
|
|
1811
|
+
CLAUDE_CONFIG_DIR, CODEX_HOME (native host configuration)
|
|
1812
|
+
AGENT_LAUNCH_VENV, ZDOTDIR
|
|
1810
1813
|
EOF
|
|
1811
1814
|
# DERIVED, not typed: the hardcoded pair went stale the moment a capability was renamed,
|
|
1812
1815
|
# and the name it still advertised selected a different capability than the one that
|
|
@@ -1814,11 +1817,10 @@ EOF
|
|
|
1814
1817
|
local known; known=$(capability_table 2>/dev/null | cut -f1 | tr '\n' ' ')
|
|
1815
1818
|
[ -n "$known" ] && printf ' --with names: %s\n' "${known% }"
|
|
1816
1819
|
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
if [ -e "$REPO/.git" ]; then
|
|
1820
|
+
if [ "${AGENT_BIOS_LEGACY_INSTALL:-0}" != 1 ]; then
|
|
1821
|
+
printf '\nRecover: agent-bios corpus history --json; submit an operation=rollback request\n'
|
|
1822
|
+
printf ' through agent-bios corpus plan/apply for a baseline_ref or history_id.\n'
|
|
1823
|
+
elif [ -e "$REPO/.git" ]; then
|
|
1822
1824
|
printf '\nRecover: python3 %s/compose/corpus-state.py list, then rollback --version V\n' "$REPO"
|
|
1823
1825
|
else
|
|
1824
1826
|
printf '\nRecover: npm install -g agent-bios@<older-version> && agent-bios install\n'
|
|
@@ -1829,9 +1831,9 @@ EOF
|
|
|
1829
1831
|
CMD="${1:-help}"
|
|
1830
1832
|
if [ $# -gt 0 ]; then shift; fi
|
|
1831
1833
|
|
|
1832
|
-
# Private installation is the
|
|
1833
|
-
#
|
|
1834
|
-
#
|
|
1834
|
+
# Private installation is the default path. The explicit legacy flag selects
|
|
1835
|
+
# compatibility installation and its regression fixtures. No private operation
|
|
1836
|
+
# falls through to a global writer.
|
|
1835
1837
|
if [ "$CMD" = "corpus" ]; then
|
|
1836
1838
|
exec python3 "$REPO/compose/corpus.py" --repo "$REPO" "$@" <&3
|
|
1837
1839
|
fi
|
|
@@ -1841,9 +1843,28 @@ fi
|
|
|
1841
1843
|
if [ "$CMD" = "understand" ]; then
|
|
1842
1844
|
exec python3 "$REPO/compose/corpus_understand.py" --repo "$REPO" "$@" <&3
|
|
1843
1845
|
fi
|
|
1846
|
+
if [ "$CMD" = "app" ]; then
|
|
1847
|
+
exec python3 "$REPO/compose/corpus_app.py" --repo "$REPO" "$@" <&3
|
|
1848
|
+
fi
|
|
1849
|
+
if [ "$CMD" = "import" ]; then
|
|
1850
|
+
exec python3 "$REPO/compose/corpus_import.py" --repo "$REPO" "$@" <&3
|
|
1851
|
+
fi
|
|
1852
|
+
if [ "$CMD" = "setup" ]; then
|
|
1853
|
+
if ! command -v python3 >/dev/null 2>&1 || ! python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))' </dev/null; then
|
|
1854
|
+
log "Python 3.11 or newer is required for conversation setup."
|
|
1855
|
+
log "Install Python with your operating system package manager, then rerun this command."
|
|
1856
|
+
exit 1
|
|
1857
|
+
fi
|
|
1858
|
+
exec python3 "$REPO/compose/corpus_setup_cli.py" --repo "$REPO" "$@" <&3
|
|
1859
|
+
fi
|
|
1844
1860
|
if [ "${AGENT_BIOS_LEGACY_INSTALL:-0}" != 1 ]; then
|
|
1845
1861
|
case "$CMD" in
|
|
1846
1862
|
install|onboard|verify|status|uninstall|migrate|reset)
|
|
1863
|
+
if ! command -v python3 >/dev/null 2>&1 || ! python3 -c 'import sys; sys.exit(sys.version_info < (3, 11))' </dev/null; then
|
|
1864
|
+
log "Python 3.11 or newer is required for the installer and dependency chooser."
|
|
1865
|
+
log "Install Python with your operating system package manager, then rerun this command."
|
|
1866
|
+
exit 1
|
|
1867
|
+
fi
|
|
1847
1868
|
exec python3 "$REPO/compose/corpus_install.py" --repo "$REPO" "$CMD" "$@" <&3
|
|
1848
1869
|
;;
|
|
1849
1870
|
esac
|
|
@@ -1857,7 +1878,15 @@ fi
|
|
|
1857
1878
|
if [ "$CMD" = "learn" ]; then
|
|
1858
1879
|
collector="$REPO/learn/collect-learning.py"
|
|
1859
1880
|
[ -f "$collector" ] || { log "learn: collector missing at $collector"; exit 1; }
|
|
1860
|
-
|
|
1881
|
+
learning_python="python3"
|
|
1882
|
+
if ! python3 -c 'from jsonschema import Draft202012Validator' </dev/null >/dev/null 2>&1; then
|
|
1883
|
+
learning_python="${AGENT_LAUNCH_VENV:-$HOME/.local/share/agent-launch/venv}/bin/python"
|
|
1884
|
+
if [ ! -x "$learning_python" ] || ! "$learning_python" -c 'from jsonschema import Draft202012Validator' </dev/null >/dev/null 2>&1; then
|
|
1885
|
+
log "learn: JSON Schema support is missing; select jsonschema in agent-bios install --interactive."
|
|
1886
|
+
exit 1
|
|
1887
|
+
fi
|
|
1888
|
+
fi
|
|
1889
|
+
exec "$learning_python" "$collector" "$@" <&3
|
|
1861
1890
|
fi
|
|
1862
1891
|
# `cost` is the same shape for the same reason: the guides tell an installed user to
|
|
1863
1892
|
# measure with session-cost.py, and on a packaged install that file lives inside the
|
package/launch/agent-launch.py
CHANGED
|
@@ -1421,6 +1421,18 @@ def parse_capability_offers(name: str, raw: Any) -> tuple:
|
|
|
1421
1421
|
return tuple(parsed)
|
|
1422
1422
|
|
|
1423
1423
|
|
|
1424
|
+
def review_wrapper_command(host: str, name: str) -> str | None:
|
|
1425
|
+
"""Resolve a review adapter from the active private package or native installation."""
|
|
1426
|
+
if private_corpus_enabled():
|
|
1427
|
+
path = corpus_package_root() / "wrappers" / f"{name}.sh"
|
|
1428
|
+
elif host == "codex":
|
|
1429
|
+
path = expand_config_path(f"${{CODEX_HOME}}/bin/{name}")
|
|
1430
|
+
else:
|
|
1431
|
+
home = os.environ.get("CLAUDE_CONFIG_DIR") or str(pathlib.Path.home() / ".claude")
|
|
1432
|
+
path = pathlib.Path(home) / "bin" / name
|
|
1433
|
+
return str(path.absolute()) if path.is_file() and os.access(path, os.X_OK) else None
|
|
1434
|
+
|
|
1435
|
+
|
|
1424
1436
|
def host_dispatch_command(host: str, config: dict[str, Any]) -> str | None:
|
|
1425
1437
|
"""The absolute command that spawns a fresh, exactly-pinned, read-only session on
|
|
1426
1438
|
`host`, or None when this machine cannot.
|
|
@@ -1430,23 +1442,12 @@ def host_dispatch_command(host: str, config: dict[str, Any]) -> str | None:
|
|
|
1430
1442
|
named the reviewer's seat but never said how to reach it — the launched agent was
|
|
1431
1443
|
told to run isolated passes on a model with no way to spawn them."""
|
|
1432
1444
|
if host == "codex":
|
|
1433
|
-
|
|
1434
|
-
return str(path.absolute()) if path.is_file() and os.access(path, os.X_OK) else None
|
|
1445
|
+
return review_wrapper_command("codex", "codex-run")
|
|
1435
1446
|
if host == "claude":
|
|
1436
|
-
# The
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
# Falls THROUGH rather than returning None when the adapter is absent, which is
|
|
1441
|
-
# the difference from codex: there, codex-run is the only route that can pin a
|
|
1442
|
-
# seat, so its absence really is no route. Here the bare binary is a working
|
|
1443
|
-
# dispatch that simply cannot produce a receipt, and taking the panel away from
|
|
1444
|
-
# a machine that has not deployed the adapter yet would be a worse trade than
|
|
1445
|
-
# the missing evidence. An un-updated install keeps exactly today's behaviour.
|
|
1446
|
-
home = os.environ.get("CLAUDE_CONFIG_DIR") or str(pathlib.Path.home() / ".claude")
|
|
1447
|
-
adapter = pathlib.Path(home) / "bin" / "claude-run"
|
|
1448
|
-
if adapter.is_file() and os.access(adapter, os.X_OK):
|
|
1449
|
-
return str(adapter.absolute())
|
|
1447
|
+
# The bare Claude backend remains a usable fallback without adapter receipts.
|
|
1448
|
+
adapter = review_wrapper_command("claude", "claude-run")
|
|
1449
|
+
if adapter is not None:
|
|
1450
|
+
return adapter
|
|
1450
1451
|
backend = config.get("backends", {}).get(host, {})
|
|
1451
1452
|
try:
|
|
1452
1453
|
return resolve_command(backend.get("command", ""))
|
|
@@ -4116,7 +4117,12 @@ def expand_config_path(value: str) -> pathlib.Path:
|
|
|
4116
4117
|
return pathlib.Path(os.path.expandvars(os.path.expanduser(expanded)))
|
|
4117
4118
|
|
|
4118
4119
|
|
|
4120
|
+
_UI_RUNTIME_RELEASE: Callable[[], None] | None = None
|
|
4121
|
+
|
|
4122
|
+
|
|
4119
4123
|
def exec_backend(command: str, args: list[str], env: dict[str, str] | None = None) -> NoReturn:
|
|
4124
|
+
if _UI_RUNTIME_RELEASE is not None:
|
|
4125
|
+
_UI_RUNTIME_RELEASE()
|
|
4120
4126
|
os.execve(command, [command, *args], os.environ.copy() if env is None else env)
|
|
4121
4127
|
|
|
4122
4128
|
|
|
@@ -4564,11 +4570,9 @@ def _resolves(value: str) -> bool:
|
|
|
4564
4570
|
|
|
4565
4571
|
def cross_native_command(plan: dict[str, Any]) -> str | None:
|
|
4566
4572
|
"""Absolute command a cross-family main dispatches to for native review, or
|
|
4567
|
-
None if unresolvable.
|
|
4568
|
-
CODEX_HOME/bin; the claude reviewer is the claude backend on PATH."""
|
|
4573
|
+
None if unresolvable. Codex uses the active review adapter; Claude uses its backend."""
|
|
4569
4574
|
if plan["review_host"] == "codex":
|
|
4570
|
-
|
|
4571
|
-
return str(path.absolute()) if path.is_file() and os.access(path, os.X_OK) else None
|
|
4575
|
+
return review_wrapper_command("codex", "codex-run")
|
|
4572
4576
|
try:
|
|
4573
4577
|
return resolve_command(plan["review_backend"])
|
|
4574
4578
|
except LaunchError:
|
|
@@ -4576,11 +4580,10 @@ def cross_native_command(plan: dict[str, Any]) -> str | None:
|
|
|
4576
4580
|
|
|
4577
4581
|
|
|
4578
4582
|
def cross_helm_command(plan: dict[str, Any]) -> str | None:
|
|
4579
|
-
"""The
|
|
4583
|
+
"""The Codex fan-out reviewer adapter from the active installation."""
|
|
4580
4584
|
if plan["review_host"] != "codex":
|
|
4581
4585
|
return None
|
|
4582
|
-
|
|
4583
|
-
return str(path) if path.is_file() and os.access(path, os.X_OK) else None
|
|
4586
|
+
return review_wrapper_command("codex", "codex-helm")
|
|
4584
4587
|
|
|
4585
4588
|
|
|
4586
4589
|
def cross_ultracode_command(plan: dict[str, Any]) -> str | None:
|
|
@@ -4857,11 +4860,54 @@ def setup_summary_lines(plan: dict[str, Any] | None) -> list[str]:
|
|
|
4857
4860
|
return lines
|
|
4858
4861
|
|
|
4859
4862
|
|
|
4863
|
+
def _activate_cli_ui_runtime() -> bool:
|
|
4864
|
+
"""Use the package's offline UI bundle; standalone compatibility copies keep their runtime."""
|
|
4865
|
+
global _UI_RUNTIME_RELEASE
|
|
4866
|
+
source = pathlib.Path(__file__).resolve().parents[1]
|
|
4867
|
+
if os.environ.get("AGENT_BIOS_PACKAGE_ROOT") or private_corpus_enabled():
|
|
4868
|
+
root = corpus_package_root()
|
|
4869
|
+
elif (source / "compose/corpus_store.py").is_file():
|
|
4870
|
+
root = source
|
|
4871
|
+
else:
|
|
4872
|
+
return False
|
|
4873
|
+
loader = root / "compose/corpus_ui_runtime.py"
|
|
4874
|
+
if loader.is_symlink() or not loader.is_file():
|
|
4875
|
+
raise LaunchError("bundled UI runtime loader is missing or unsafe; reinstall the agent-bios package")
|
|
4876
|
+
module_root = str(root / "compose")
|
|
4877
|
+
if module_root not in sys.path:
|
|
4878
|
+
sys.path.insert(0, module_root)
|
|
4879
|
+
try:
|
|
4880
|
+
from corpus_ui_runtime import activate_ui_runtime, release_ui_runtime
|
|
4881
|
+
activate_ui_runtime(root)
|
|
4882
|
+
except (ImportError, OSError, RuntimeError) as exc:
|
|
4883
|
+
raise LaunchError(f"bundled terminal UI could not start: {exc}") from exc
|
|
4884
|
+
_UI_RUNTIME_RELEASE = release_ui_runtime
|
|
4885
|
+
return True
|
|
4886
|
+
|
|
4887
|
+
|
|
4888
|
+
def _textual_api() -> tuple[Any, ...]:
|
|
4889
|
+
"""Import the public UI APIs used by this launcher without starting an app."""
|
|
4890
|
+
from textual import work
|
|
4891
|
+
from textual.app import App
|
|
4892
|
+
from textual.binding import Binding
|
|
4893
|
+
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
4894
|
+
from textual.screen import ModalScreen
|
|
4895
|
+
from textual.widgets import Input, OptionList, Static
|
|
4896
|
+
from textual.widgets.option_list import Option
|
|
4897
|
+
from rich.text import Text
|
|
4898
|
+
from textual.theme import Theme
|
|
4899
|
+
return (work, App, Binding, Horizontal, Vertical, VerticalScroll, ModalScreen,
|
|
4900
|
+
Input, OptionList, Static, Option, Text, Theme)
|
|
4901
|
+
|
|
4902
|
+
|
|
4860
4903
|
def textual_importable() -> bool:
|
|
4861
4904
|
try:
|
|
4862
|
-
|
|
4863
|
-
except
|
|
4864
|
-
|
|
4905
|
+
_textual_api()
|
|
4906
|
+
except ImportError as exc:
|
|
4907
|
+
if any(exc.name == name or (exc.name or "").startswith(name + ".")
|
|
4908
|
+
for name in ("textual", "rich")):
|
|
4909
|
+
return False
|
|
4910
|
+
raise
|
|
4865
4911
|
return True
|
|
4866
4912
|
|
|
4867
4913
|
|
|
@@ -4926,15 +4972,8 @@ _UI_CANCEL = "\x00cancel"
|
|
|
4926
4972
|
def _build_app_class():
|
|
4927
4973
|
"""Import textual lazily and build the App/Screen classes, so importing this
|
|
4928
4974
|
module and every non-interactive path stays free of the textual dependency."""
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
from textual.binding import Binding
|
|
4932
|
-
from textual.containers import Horizontal, Vertical, VerticalScroll
|
|
4933
|
-
from textual.screen import ModalScreen
|
|
4934
|
-
from textual.widgets import Input, OptionList, Static
|
|
4935
|
-
from textual.widgets.option_list import Option
|
|
4936
|
-
from rich.text import Text
|
|
4937
|
-
from textual.theme import Theme
|
|
4975
|
+
(work, App, Binding, Horizontal, Vertical, VerticalScroll, ModalScreen,
|
|
4976
|
+
Input, OptionList, Static, Option, Text, Theme) = _textual_api()
|
|
4938
4977
|
|
|
4939
4978
|
# Host-matched palettes so the preflight reads as the CLI it launches.
|
|
4940
4979
|
# Both are taken from the host's own artifact: Codex from its ~/.codex dark
|
|
@@ -8913,9 +8952,10 @@ def run_corpus_apply(selection: list[str]) -> None:
|
|
|
8913
8952
|
this function only streams it and reports the exit."""
|
|
8914
8953
|
status = load_corpus_status() or {}
|
|
8915
8954
|
domains = ",".join(selection) if selection else "none"
|
|
8955
|
+
interaction = [] if os.environ.get("AGENT_BIOS_LEGACY_INSTALL") == "1" else ["--non-interactive"]
|
|
8916
8956
|
front = shutil.which("agent-bios")
|
|
8917
8957
|
if front:
|
|
8918
|
-
argv = [front, "onboard", "--domains", domains]
|
|
8958
|
+
argv = [front, "onboard", *interaction, "--domains", domains]
|
|
8919
8959
|
else:
|
|
8920
8960
|
# isinstance, not truthiness: `repo` is whatever the JSON holds, and a non-string
|
|
8921
8961
|
# truthy value reached pathlib.Path() and raised TypeError out of the apply path.
|
|
@@ -8928,11 +8968,11 @@ def run_corpus_apply(selection: list[str]) -> None:
|
|
|
8928
8968
|
if installer is None or not installer.is_file():
|
|
8929
8969
|
print(
|
|
8930
8970
|
"agent-launch: no agent-bios on PATH and no usable repo in "
|
|
8931
|
-
f"corpus-status.json; run manually: agent-bios onboard --domains {domains}",
|
|
8971
|
+
f"corpus-status.json; run manually: agent-bios onboard {' '.join(interaction)} --domains {domains}",
|
|
8932
8972
|
file=sys.stderr,
|
|
8933
8973
|
)
|
|
8934
8974
|
return
|
|
8935
|
-
argv = ["bash", str(installer), "onboard", "--domains", domains]
|
|
8975
|
+
argv = ["bash", str(installer), "onboard", *interaction, "--domains", domains]
|
|
8936
8976
|
print(f"\nagent-launch: applying corpus selection: {' '.join(argv)}\n", flush=True)
|
|
8937
8977
|
proc = subprocess.run(argv)
|
|
8938
8978
|
if proc.returncode != 0:
|
|
@@ -11008,7 +11048,7 @@ def _tolerate_narrow_stdout() -> None:
|
|
|
11008
11048
|
pass
|
|
11009
11049
|
|
|
11010
11050
|
|
|
11011
|
-
def main(argv: list[str]) -> int:
|
|
11051
|
+
def main(argv: list[str], *, bundled_ui: bool = False) -> int:
|
|
11012
11052
|
_tolerate_narrow_stdout()
|
|
11013
11053
|
args = parse_args(drop_check_adapter_separator(argv))
|
|
11014
11054
|
if args.corpus:
|
|
@@ -11142,14 +11182,18 @@ def main(argv: list[str]) -> int:
|
|
|
11142
11182
|
and not rich_ui_declined
|
|
11143
11183
|
and os.environ.get("TERM", "") not in {"", "dumb"}
|
|
11144
11184
|
)
|
|
11145
|
-
if use_textual
|
|
11146
|
-
|
|
11185
|
+
if use_textual:
|
|
11186
|
+
bundled = bundled_ui and _activate_cli_ui_runtime()
|
|
11147
11187
|
if not textual_importable():
|
|
11148
|
-
|
|
11149
|
-
|
|
11150
|
-
|
|
11151
|
-
|
|
11152
|
-
|
|
11188
|
+
if bundled:
|
|
11189
|
+
raise LaunchError("bundled UI APIs are unavailable; reinstall the agent-bios package")
|
|
11190
|
+
maybe_reexec_into_venv()
|
|
11191
|
+
if not textual_importable():
|
|
11192
|
+
use_textual = False
|
|
11193
|
+
print(
|
|
11194
|
+
"agent-launch: rich terminal UI unavailable; using numbered prompts.",
|
|
11195
|
+
file=sys.stderr,
|
|
11196
|
+
)
|
|
11153
11197
|
while True:
|
|
11154
11198
|
try:
|
|
11155
11199
|
if args.understand:
|
|
@@ -11289,7 +11333,7 @@ def main(argv: list[str]) -> int:
|
|
|
11289
11333
|
|
|
11290
11334
|
if __name__ == "__main__":
|
|
11291
11335
|
try:
|
|
11292
|
-
raise SystemExit(main(sys.argv[1:]))
|
|
11336
|
+
raise SystemExit(main(sys.argv[1:], bundled_ui=True))
|
|
11293
11337
|
except LaunchError as exc:
|
|
11294
11338
|
print(f"agent-launch: {exc}", file=sys.stderr)
|
|
11295
11339
|
raise SystemExit(2)
|
package/launch/provision-venv.sh
CHANGED
|
@@ -1,28 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Provision
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# maybe_reexec_into_venv). It is an enhancement, not a hard dependency: when the
|
|
6
|
-
# venv is missing or broken, the launcher falls back to numbered prompts, and
|
|
7
|
-
# every direct / non-TTY / --no-tui path keeps running under the system
|
|
8
|
-
# interpreter untouched. Re-run this after upgrading Python or the pin below.
|
|
2
|
+
# Provision selected dependencies in the managed agent-bios virtualenv.
|
|
3
|
+
# No argument installs Textual for terminal interfaces. --learning-only installs
|
|
4
|
+
# the JSON Schema validator used by learn. Existing other packages are preserved.
|
|
9
5
|
#
|
|
10
6
|
# AGENT_LAUNCH_VENV override the venv location (default below)
|
|
11
7
|
# AGENT_LAUNCH_PYTHON python used to create the venv (default: python3)
|
|
12
8
|
set -eu
|
|
13
9
|
|
|
14
10
|
TEXTUAL_PIN="textual==8.2.8"
|
|
11
|
+
JSONSCHEMA_PIN="jsonschema==4.26.0"
|
|
15
12
|
VENV="${AGENT_LAUNCH_VENV:-$HOME/.local/share/agent-launch/venv}"
|
|
16
13
|
PYTHON="${AGENT_LAUNCH_PYTHON:-python3}"
|
|
14
|
+
PACKAGES=("$TEXTUAL_PIN")
|
|
15
|
+
case "${1:-}" in
|
|
16
|
+
"") [ "$#" -eq 0 ] || { printf 'usage: provision-venv.sh [--learning-only]\n' >&2; exit 2; } ;;
|
|
17
|
+
--learning-only) [ "$#" -eq 1 ] || { printf 'usage: provision-venv.sh [--learning-only]\n' >&2; exit 2; }; PACKAGES=("$JSONSCHEMA_PIN") ;;
|
|
18
|
+
*) printf 'usage: provision-venv.sh [--learning-only]\n' >&2; exit 2 ;;
|
|
19
|
+
esac
|
|
20
|
+
|
|
21
|
+
runtime_python_ready() {
|
|
22
|
+
"$1" -c 'import sys; sys.exit("agent-bios requires Python 3.11+; select it with AGENT_LAUNCH_PYTHON and use a new AGENT_LAUNCH_VENV path for an incompatible existing environment" if sys.version_info < (3, 11) else 0)'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
pinned_ready() {
|
|
26
|
+
"$VENV/bin/python" - "${PACKAGES[@]}" <<'PY'
|
|
27
|
+
import importlib
|
|
28
|
+
import importlib.metadata
|
|
29
|
+
import sys
|
|
30
|
+
for spec in sys.argv[1:]:
|
|
31
|
+
name, expected = spec.split("==", 1)
|
|
32
|
+
importlib.import_module(name)
|
|
33
|
+
if name == "jsonschema":
|
|
34
|
+
from jsonschema import Draft202012Validator
|
|
35
|
+
actual = importlib.metadata.version(name)
|
|
36
|
+
if actual != expected:
|
|
37
|
+
raise SystemExit(f"{name}: expected {expected}, found {actual}")
|
|
38
|
+
print(f" {name} {actual} | python {sys.version.split()[0]}")
|
|
39
|
+
PY
|
|
40
|
+
}
|
|
17
41
|
|
|
18
|
-
if [ -x "$VENV/bin/python" ]
|
|
42
|
+
if [ -x "$VENV/bin/python" ]; then
|
|
43
|
+
runtime_python_ready "$VENV/bin/python"
|
|
44
|
+
else
|
|
45
|
+
runtime_python_ready "$PYTHON"
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
if [ -x "$VENV/bin/python" ] && pinned_ready >/dev/null 2>&1; then
|
|
19
49
|
printf 'agent-launch venv already provisioned: %s\n' "$VENV"
|
|
20
|
-
|
|
50
|
+
pinned_ready
|
|
21
51
|
exit 0
|
|
22
52
|
fi
|
|
23
53
|
|
|
24
54
|
printf 'Provisioning agent-launch venv at %s ...\n' "$VENV"
|
|
25
|
-
|
|
26
|
-
"$
|
|
27
|
-
|
|
28
|
-
"$VENV/bin/python" -
|
|
55
|
+
if [ ! -x "$VENV/bin/python" ]; then
|
|
56
|
+
"$PYTHON" -m venv "$VENV"
|
|
57
|
+
fi
|
|
58
|
+
"$VENV/bin/python" -m pip install --quiet --force-reinstall "${PACKAGES[@]}"
|
|
59
|
+
pinned_ready
|
|
@@ -23,7 +23,9 @@ never patches the payload to make it pass (runtime enforces, does not reason).
|
|
|
23
23
|
|
|
24
24
|
Input: the semantic payload as one JSON object on stdin.
|
|
25
25
|
Host: --host claude|codex (default: the tool prefix of supporting_sessions[0]).
|
|
26
|
-
|
|
26
|
+
Private storage: $AGENT_BIOS_CORPUS_DIR, else ~/.config/agent-bios/corpus.
|
|
27
|
+
Legacy storage: --config-dir, else $CLAUDE_CONFIG_DIR / $CODEX_HOME by host.
|
|
28
|
+
--config-dir requires AGENT_BIOS_LEGACY_INSTALL=1; it cannot relocate private capture.
|
|
27
29
|
After the local writes it best-effort uploads not-yet-delivered records to
|
|
28
30
|
{base}/api/ingest/learnings via a host-private watermark over events.jsonl. The base and
|
|
29
31
|
token resolve from the agent-bios-owned slot ~/.config/agent-bios/{ingest-url,
|
|
@@ -298,12 +300,12 @@ def apply_codex(home, bullet, dry):
|
|
|
298
300
|
APPLY = {"claude": apply_claude, "codex": apply_codex}
|
|
299
301
|
|
|
300
302
|
|
|
301
|
-
# ── Phase 2 transport: watermark upload over
|
|
303
|
+
# ── Phase 2 transport: watermark upload over durable learning records ───────
|
|
302
304
|
#
|
|
303
305
|
# collect-learning runs on-demand (per `learn!`), so the upload piggybacks here:
|
|
304
306
|
# after the local writes, best-effort POST any not-yet-settled records to
|
|
305
307
|
# {ingest-url}/api/ingest/learnings and record which learning_ids are settled in
|
|
306
|
-
# a small state file (the watermark). The durable
|
|
308
|
+
# a small state file (the watermark). The durable event log is the single
|
|
307
309
|
# source and is never capped, so nothing is lost across a multi-day server outage
|
|
308
310
|
# — unsettled records simply retry on the next `learn!`. A 2xx (incl. a duplicate
|
|
309
311
|
# re-send, which the server dedups by learning_id) settles a record; 400/413
|
|
@@ -1143,11 +1145,13 @@ def _self_test():
|
|
|
1143
1145
|
|
|
1144
1146
|
|
|
1145
1147
|
def main():
|
|
1148
|
+
sys.dont_write_bytecode = True
|
|
1146
1149
|
ap = argparse.ArgumentParser(description="Submit a session learning (learn!).")
|
|
1147
1150
|
ap.add_argument("--host", choices=sorted(HOSTS),
|
|
1148
1151
|
help="session host (default: tool prefix of supporting_sessions[0])")
|
|
1149
1152
|
ap.add_argument("--config-dir", default=None,
|
|
1150
|
-
help="
|
|
1153
|
+
help="legacy host home only (requires AGENT_BIOS_LEGACY_INSTALL=1); "
|
|
1154
|
+
"private capture uses AGENT_BIOS_CORPUS_DIR")
|
|
1151
1155
|
ap.add_argument("--dry-run", action="store_true",
|
|
1152
1156
|
help="validate and print actions; write nothing")
|
|
1153
1157
|
ap.add_argument("--no-upload", action="store_true",
|
|
@@ -1160,6 +1164,11 @@ def main():
|
|
|
1160
1164
|
_self_test()
|
|
1161
1165
|
return
|
|
1162
1166
|
|
|
1167
|
+
legacy = os.environ.get("AGENT_BIOS_LEGACY_INSTALL") == "1"
|
|
1168
|
+
if args.config_dir is not None and not legacy:
|
|
1169
|
+
ap.error("--config-dir applies only to AGENT_BIOS_LEGACY_INSTALL=1; "
|
|
1170
|
+
"set AGENT_BIOS_CORPUS_DIR to relocate private learning storage")
|
|
1171
|
+
|
|
1163
1172
|
payload = read_payload()
|
|
1164
1173
|
host = resolve_host(args.host, payload)
|
|
1165
1174
|
home = resolve_home(host, args.config_dir)
|
|
@@ -1173,7 +1182,7 @@ def main():
|
|
|
1173
1182
|
sys.exit(1)
|
|
1174
1183
|
|
|
1175
1184
|
dry = args.dry_run
|
|
1176
|
-
if
|
|
1185
|
+
if not legacy:
|
|
1177
1186
|
# Capture is an immutable private source. Model-visible projections are
|
|
1178
1187
|
# composed when an activated session is started, never written globally.
|
|
1179
1188
|
sys.path.insert(0, str(REPO / "compose"))
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"learning_id": {
|
|
22
22
|
"type": "string",
|
|
23
23
|
"pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
24
|
-
"description": "Client-generated UUID for this learning, lowercase canonical form (tooling must lowercase, e.g. macOS uuidgen emits uppercase). Owned by learn/collect-learning.py, never hand-authored. Idempotency key for the
|
|
24
|
+
"description": "Client-generated UUID for this learning, lowercase canonical form (tooling must lowercase, e.g. macOS uuidgen emits uppercase). Owned by learn/collect-learning.py, never hand-authored. Idempotency key for the bounded upload retry over the host-qualified events.jsonl. Distinct from the ledger's `id` (a curator-assigned short id like 'S3-07'); this is the light-flow record's own key.",
|
|
25
25
|
"$comment": "Ledger entries use `id`; the light-flow record's own key is `learning_id` (the artifact is a learning — LEXICON.md — so it is not `distill_id`, which would collide with the heavy session-distill pipeline)."
|
|
26
26
|
},
|
|
27
27
|
"lesson": {
|
|
28
28
|
"type": "string",
|
|
29
29
|
"minLength": 8,
|
|
30
30
|
"maxLength": 2000,
|
|
31
|
-
"description": "The
|
|
31
|
+
"description": "The captured lesson prose stored in the private corpus's host-qualified events.jsonl and supplied to future selected, activated-session snapshots. Ledger-compatible with entries[].lesson."
|
|
32
32
|
},
|
|
33
33
|
"domain": {
|
|
34
34
|
"type": "string",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-bios",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"releaseDate": "2026-09-
|
|
3
|
+
"version": "0.19.0",
|
|
4
|
+
"releaseDate": "2026-09-13",
|
|
5
5
|
"description": "A thin, low-level instruction layer for LLM CLI agents: one set of principles and behavior whichever model you run. Stores a private, editable corpus for explicitly activated sessions.",
|
|
6
6
|
"bin": {
|
|
7
7
|
"agent-bios": "install.sh"
|
|
@@ -39,9 +39,19 @@
|
|
|
39
39
|
"compose/corpus_catalog.py",
|
|
40
40
|
"compose/corpus_session.py",
|
|
41
41
|
"compose/corpus_install.py",
|
|
42
|
+
"compose/corpus_setup.py",
|
|
43
|
+
"compose/corpus_setup_cli.py",
|
|
44
|
+
"compose/corpus_setup_i18n.py",
|
|
45
|
+
"compose/corpus_setup_ui.py",
|
|
46
|
+
"compose/corpus_ui_runtime.py",
|
|
47
|
+
"compose/ui_runtime/",
|
|
48
|
+
"compose/corpus_import.py",
|
|
49
|
+
"compose/corpus_app.py",
|
|
50
|
+
"compose/app_bridge/",
|
|
42
51
|
"compose/corpus_transaction.py",
|
|
43
52
|
"compose/corpus_understand.py",
|
|
44
53
|
"compose/bootstrap/",
|
|
54
|
+
"compose/setup/START.md",
|
|
45
55
|
"launch/check-prompting-targets.sh",
|
|
46
56
|
"learn/check-learning.py",
|
|
47
57
|
"learn/collect-learning.py",
|
|
@@ -53,7 +63,9 @@
|
|
|
53
63
|
"install.sh",
|
|
54
64
|
"launch/provision-venv.sh",
|
|
55
65
|
"session-cost.py",
|
|
66
|
+
"docs/",
|
|
56
67
|
"README.md",
|
|
68
|
+
"INSTALL.md",
|
|
57
69
|
"DEPENDENCIES.md",
|
|
58
70
|
"provenance.json"
|
|
59
71
|
],
|
package/provenance.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commit":"
|
|
1
|
+
{"commit":"35c75ca6ee0c7ecd3ed67c58dc48b854bc2d8b8e","committedAt":"2026-09-13T14:12:08+09:00","dirty":false}
|