Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
The 0.20.0 beta line follows the frozen operator contract; earlier alpha releases
remain historical snapshots. Beta does not mean error-free operation.

## [Unreleased]

### Added

- Add an explicit, read-only `talos import ... --dry-run` preview for portable
Hermes and OpenClaw profile text, memory and Agent Skills. The preview writes
nothing, never activates imported instructions, and excludes secrets,
permissions, executable configuration, schedules, sessions and databases.

## [0.20.0-rc.2] — 2026-11-01

### Security
Expand Down
8 changes: 6 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ preferences live in `USER.md`. All three are operator-owned prompt state and rel
|---|---|
| Gate path | `policy.py`, **996 lines** — has to stay readable in one sitting |
| Tools | **37**, every one gated |
| Suites | **3456** collected tests · **277** adversarial · 44 end-to-end |
| Suites | **3477** collected tests · **277** adversarial · 44 end-to-end |
| Home | <https://talos-agent.ch> · docs at `/docs/` |
| Repository | `talos-kernel/talos` is the public source tree |

Expand Down Expand Up @@ -52,6 +52,10 @@ automatically; do not overwrite operator state with repository templates. Homebr
installations update through `brew upgrade talos-kernel/tap/talos`; `talos update`
refuses them. Source installations retain their own profile and update path.

`talos import hermes|openclaw --source <path> --dry-run` can explicitly preview a
small portable allowlist from another agent home. It changes neither home, imports
no authority or secrets, and does not activate the persistent text or Agent Skills.

The source branch can contain fixes newer than the latest signed release. Check
`CHANGELOG.md`, the release tag and `site/status.json` before claiming a fix is shipped;
a source commit does not change an existing signed archive.
Expand Down Expand Up @@ -313,7 +317,7 @@ repairs; corrections never replay an effect or grant permission. Keep those prop
```bash
python3 -m venv .venv && . .venv/bin/activate && pip install --require-hashes -r requirements.lock -r requirements-dev.lock

python -m pytest tests/ -q # 3456 collected tests; platform-specific skips are explicit
python -m pytest tests/ -q # 3477 collected tests; platform-specific skips are explicit
python redteam.py # 277 adversarial cases — mandatory for any kernel change
python e2e.py # 44 cases against a real model (costs tokens and time)
python -m talos --once # single cycle, for diagnosis
Expand Down
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
</p>

<p align="center">
<!-- ⚠️ Bewusst „tests“, nicht „passing“: die Zahl kommt aus dem Einsammeln (3456).
<!-- ⚠️ Bewusst „tests“, nicht „passing“: die Zahl kommt aus dem Einsammeln (3477).
Plattformabhaengige Sandbox- und Repository-Pruefungen koennen uebersprungen werden;
`test_site_claims` prueft deshalb die gesammelte Zahl statt ein Umgebungsresultat. -->
<img src="https://img.shields.io/badge/tests-3456-2e7d32.svg" alt="Tests">
<img src="https://img.shields.io/badge/tests-3477-2e7d32.svg" alt="Tests">
<img src="https://img.shields.io/badge/red%20team-277%2F277-2e7d32.svg" alt="Red team">
<img src="https://img.shields.io/badge/gate%20path-996%20lines-8a4318.svg" alt="Gate path">
<img src="https://img.shields.io/badge/tools-37%20gated-8a4318.svg" alt="Tools">
Expand Down Expand Up @@ -258,6 +258,13 @@ section atomically with mode 0600. It prints the configuration path and the next
Use `talos doctor` for missing prerequisites and `talos help <command>` for command help.
A running service needs a restart after changing its file settings.

Coming from Hermes or OpenClaw? `talos import` produces a read-only, redacted plan for
portable profile files, memory documents and Agent Skills. It does not copy anything in
this release. Secrets, approvals, command allowlists, executable plugins and MCP commands,
schedules, sessions and databases are skipped without reading their values. Every proposed
skill remains inactive until a later, separately reviewed activation path passes Talos'
existing approval boundary. See [the migration preview contract](docs/import-agents.md).

Later, a single part can be redone without repeating the rest:

```bash
Expand All @@ -271,6 +278,9 @@ python -m talos config set TALOS_MODEL_OVERRIDES '{"local-model": {"context_wind
# a window or a price the catalogue does not know —
# never a provider, url or key; shown as your word
python -m talos models --refresh # force the same provider-catalogue sync now
python -m talos import list # supported agent-home previews
python -m talos import hermes --source "$HOME/.hermes" --dry-run
python -m talos import openclaw --source "$HOME/.openclaw" --dry-run
python -m talos status # what it did last
python -m talos chat # a session here; approvals at a terminal
python -m talos ask "how many …?" # one turn from here, answer on stdout
Expand Down Expand Up @@ -882,13 +892,14 @@ command centre.
`/allowed` `/revoke` · `/log` `/undo` `/policy` `/autonomy` `/tools` `/whoami` `/version` ·
`/usage` `/model` `/eval` `/reasoning` `/debug`

**On the command line** — thirteen, each answering a question an operator actually asks:
**On the command line** — each answering a question an operator actually asks:

| | |
|---|---|
| `chat` | a session here; approvals at a terminal |
| `ask "…"` | one turn, for scripts and cron |
| `setup` · `doctor` · `config` · `models` | set it up, and find out why something won't work |
| `import list` · `import <hermes\|openclaw> --source … --dry-run` | preview portable agent data without writing or activating it |
| `status` · `events` · `why <id>` | what it did, and why that was allowed or refused |
| `report` · `review` | a record someone else can read · what this installation should change |
| `update` · `version` | signature checked, tests first |
Expand Down Expand Up @@ -925,6 +936,7 @@ sitting — a gate you cannot read is not a gate.
| `vision.py` / `hearing.py` / `speech.py` / `frames.py` | reading a picture, hearing a recording, speaking, one still out of a video — ordinary READ/WRITE with a target |
| `cli.py` / `doctor.py` / `configcli.py` / `schema.py` | the subcommands: diagnose, read and change settings — the schema decides what may be written, and the allowlist and the network exceptions never are |
| `askcli.py` | `talos ask` — one turn from a script, as a channel with no special right |
| `import_agents.py` | bounded, redacted, zero-write migration previews; imported text remains inactive |
| `models.py` / `model_catalog_sync.py` | provider-bound live model lists; complete snapshots may replace membership, incomplete ones only add, and failures keep the last good cache |
| `updater.py` | update beside the old tree, both suites in the new one, switch only if green |
| `eventlog.py` / `memory.py` / `usage.py` | durable log, conversation memory, metering |
Expand Down
40 changes: 40 additions & 0 deletions docs/import-agents.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Import preview from Hermes or OpenClaw

`talos import` answers one question: which portable parts of an existing agent home could
be reviewed for Talos? The current command is deliberately a dry run. It creates no target
directory, backup, report or temporary file and never changes the source.

```bash
talos import list
talos import hermes --source "$HOME/.hermes" --dry-run
talos import openclaw --source "$HOME/.openclaw" --dry-run
```

The source path is explicit. Talos accepts an absolute path after shell expansion and
refuses a source or target boundary containing a symlink. Input files must be bounded,
regular, singly linked UTF-8 files. The preview contains relative paths, byte counts and
SHA-256 digests, never file bodies.

## What the preview considers

- `SOUL.md`, `AGENTS.md`, `USER.md` and `MEMORY.md`, where that source stores them.
- Agent Skills with a valid `SKILL.md`, including one category-directory layer.
- Existing staging destinations as conflicts. The preview never overwrites them.

Every candidate points below `data/import-staging/<source>/`. `staged` means “eligible for
operator review”, not active or trusted. Imported text can contain hostile instructions.
It grants no permission, and a skill must still pass Talos' managed-skill approval path
before it can become active.

## What is always skipped

- API keys, tokens, OAuth state, `.env`, `auth.json` and auth profiles.
- Command allowlists, approvals, standing permissions and autonomy settings.
- Plugins, hooks, MCP commands and other executable configuration.
- Schedules, cron jobs, plans, sessions, logs and databases.

Credentials are configured again through Talos' own setup flow. Permissions are never
migrated. External connector grants must be paired separately for Talos and are never
copied from Hermes or OpenClaw. A future apply command needs its own backup, atomic
publication, exact-item confirmation, read-back and rollback contract; this preview does
not imply that contract already exists.
4 changes: 2 additions & 2 deletions site/console.html
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ <h1 id="main-content" tabindex="-1">Witness one deed from door to ledger.</h1>
<a class="btn ghost" href="#measured">This candidate, measured</a>
</div>
<div class="meter-row">
<span><b>3456</b>automated tests</span>
<span><b>3477</b>automated tests</span>
<span><b>277/277</b>adversarial</span>
<span><b>996</b>lines · gate</span>
<span><b>37</b>gated tools</span>
Expand Down Expand Up @@ -554,7 +554,7 @@ <h2 class="rv">Numbers this candidate must ship.</h2>

<div class="stats" id="releaseStats">
<div class="stat rv"><div class="n" data-f="version">0.20.0-rc.2</div><div class="l">release candidate</div><div class="sub-l" data-f="installer_version">public installer v0.20.0-rc.2</div></div>
<div class="stat rv"><div class="n" data-f="tests">3456</div><div class="l">automated tests, collected by pytest</div></div>
<div class="stat rv"><div class="n" data-f="tests">3477</div><div class="l">automated tests, collected by pytest</div></div>
<div class="stat rv"><div class="n" data-f="redteam_faelle">277</div><div class="l">adversarial cases, all behaving</div></div>
<div class="stat rv"><div class="n" data-f="kernel_zeilen">996</div><div class="l">lines in policy.py, readable in one sitting</div></div>
<div class="stat rv"><div class="n" data-f="werkzeuge">37</div><div class="l">tools, every one through the gate</div></div>
Expand Down
2 changes: 1 addition & 1 deletion site/docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ <h1 id="main-content" tabindex="-1">Talos AI agent: the field manual</h1>
<p class="lede">That one decision shapes the rest of this manual. It is why the
command list is short, and why a few obvious conveniences are missing on purpose.</p>
<div class="spec-row">
<div class="spec"><b>3456</b>automated tests</div>
<div class="spec"><b>3477</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down
2 changes: 1 addition & 1 deletion site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,7 @@ <h2 class="rv">Measured, with the path stated</h2>
<p class="sub rv">None of this asks for belief. The installer runs the unit and
adversarial suites in front of you; the real-model cases are a separate opt-in E2E run.</p>
<p class="source-caption">Release-candidate source and signed release. Counts describe the source test suite, not speed or real-world success rates; signed release contents follow their tag.</p><div class="stats">
<div class="stat rv"><div class="n cnt" data-n="3456">0</div><div class="l">automated tests collected from the source tree</div></div>
<div class="stat rv"><div class="n cnt" data-n="3477">0</div><div class="l">automated tests collected from the source tree</div></div>
<div class="stat rv"><div class="n cnt" data-n="277">0</div><div class="l">adversarial cases trying to pass the kernel</div></div>
<div class="stat rv"><div class="n cnt" data-n="44">0</div><div class="l">opt-in end-to-end cases against a real model</div></div>
<div class="stat rv"><div class="n cnt" data-n="996">0</div><div class="l">lines in the file that decides every verdict</div></div>
Expand Down
2 changes: 1 addition & 1 deletion site/llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

- Permission kernel: 996 lines of Python, the only code that decides
- 37 tools, all gated; unattended runs cannot escalate
- 3456 tests collected in the source tree; 277/277 red-team cases passed in source verification. Release contents follow their own signed tag.
- 3477 tests collected in the source tree; 277/277 red-team cases passed in source verification. Release contents follow their own signed tag.
- MCP servers run only inside a UID-separated worker, from an operator-owned registry — no marketplace, no third-party code in the agent process
- Channels: Telegram, mail (IMAP pull), CLI, WhatsApp via an operator-controlled broker — every channel fetches; no inbound port, no webhook
- Updates: signed tarballs, verified before anything is unpacked; dependencies pinned and hashed, installed with --require-hashes
Expand Down
2 changes: 1 addition & 1 deletion site/redteam/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ <h1 id="main-content" tabindex="-1">277 ways to break an agent.</h1>
<div class="spec"><b class="red">25</b>attack surfaces</div>
<div class="spec"><b>996</b>kernel lines</div>
<div class="spec"><b>37</b>gated tools</div>
<div class="spec"><b>3456</b>automated tests</div>
<div class="spec"><b>3477</b>automated tests</div>
<div class="spec"><b class="red">0</b>passes sold as safety</div>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion site/registry/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ <h1 id="main-content" tabindex="-1">The MCP registry</h1>
guide with the full protocol is <a href="https://github.com/talos-kernel/talos/blob/master/docs/claude-worker.md">docs/claude-worker.md</a>.
A curated starting point ships as <code>examples/mcp-servers.json</code>.</p>
<div class="spec-row">
<div class="spec"><b>3456</b>automated tests</div>
<div class="spec"><b>3477</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down
2 changes: 1 addition & 1 deletion site/setup/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ <h1 id="main-content" tabindex="-1" class="rv">This address moved.</h1>
<a class="btn ghost" href="/">Return to the keep</a>
</div>
<div class="spec-row rv">
<div class="spec"><b>3456</b>automated tests</div>
<div class="spec"><b>3477</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>37</b>gated tools</div>
Expand Down
2 changes: 1 addition & 1 deletion site/status.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"release_state": "released",
"source_state": "release",
"installer_version": "0.20.0-rc.2",
"tests": 3456,
"tests": 3477,
"redteam_faelle": 277,
"kernel_zeilen": 996,
"werkzeuge": 37,
Expand Down
2 changes: 1 addition & 1 deletion site/vergleich/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ <h1 id="main-content" tabindex="-1">The others can do more.</h1>
<div class="spec-row">
<div class="spec"><b>37</b>tools here, every one gated</div>
<div class="spec"><b>100+</b>skills there · counted honestly</div>
<div class="spec"><b>3456</b>automated tests</div>
<div class="spec"><b>3477</b>automated tests</div>
<div class="spec"><b>277/277</b>adversarial cases</div>
<div class="spec"><b>996</b>gate lines</div>
<div class="spec"><b>0</b>inbound ports</div>
Expand Down
63 changes: 62 additions & 1 deletion talos/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
setup [terminal|identity|model|mail] guided setup; preserves other settings
doctor [--online] what is missing — changes nothing
config list|get|set|describe|validate find, explain and change settings
import list|hermes|openclaw preview portable data from another agent
models [--refresh] which models a provider offers
local-check [--provider ollama|lm-studio] probe local endpoint without operator data

Expand Down Expand Up @@ -266,6 +267,65 @@ def cmd_chat(rest: list[str], out=None) -> int:
return 0


def cmd_import(rest: list[str], out=None) -> int:
"""Preview portable data from another agent without changing either home.

Import text is persistent, foreign instruction material. The first release
therefore stops at a redacted plan: activation needs a separate reviewed path,
especially for skills. Keeping this command before configuration loading also
lets a new Talos installation inspect its old home before setup.
"""
schreiben = (out or sys.stdout).write
usage = " usage: talos import <hermes|openclaw> --source <absolute-path> --dry-run\n"
if rest in (["--help"], ["-h"]):
schreiben(
usage
+ " talos import list\n"
+ " Preview only. Secrets, permissions, executable hooks, schedules and sessions are skipped.\n"
)
return 0
if rest == ["list"]:
schreiben(
"\n hermes portable profile, memory and Agent Skills\n"
" openclaw portable workspace profile, memory and Agent Skills\n\n"
" Dry run only; imported text remains inactive and grants no permission.\n\n"
)
return 0
if not rest or rest[0] not in {"hermes", "openclaw"}:
schreiben(usage)
return 2
source_type = rest[0]
source = ""
dry_run = False
index = 1
while index < len(rest):
argument = rest[index]
if argument == "--source" and index + 1 < len(rest) and not source:
source = rest[index + 1]
index += 2
continue
if argument == "--dry-run" and not dry_run:
dry_run = True
index += 1
continue
schreiben(f" unknown or repeated import option: {argument}\n{usage}")
return 2
if not source or not dry_run:
schreiben(usage + " This release requires an explicit source and supports preview only.\n")
return 2

from .import_agents import ImportPlanError, plan_import
from .paths import PROFILE_DIR

try:
plan = plan_import(source, PROFILE_DIR, source_type=source_type)
except (ImportPlanError, OSError) as error:
schreiben(f"\n import preview refused: {error}\n\n")
return 1
schreiben("\n" + plan.render() + "\n\n")
return 0


def _help(_rest: list[str]) -> int:
from .terminalui import heading, help_text

Expand All @@ -281,6 +341,7 @@ def _help(_rest: list[str]) -> int:
"setup": _lazy("setup_wizard", "run_setup"),
"doctor": _lazy("doctor", "run_doctor"),
"config": _lazy("configcli", "run_config"),
"import": cmd_import,
"models": _lazy("models", "run_models"),
"local-check": _lazy("localcheck", "run_local_check"),
"ask": cmd_ask,
Expand Down Expand Up @@ -337,4 +398,4 @@ def dispatch(args: list[str]) -> int | None:

COMMANDS = tuple(TABLE)

__all__ = ["COMMANDS", "HELP", "TABLE", "cmd_status", "cmd_verify", "cmd_version", "dispatch"]
__all__ = ["COMMANDS", "HELP", "TABLE", "cmd_import", "cmd_status", "cmd_verify", "cmd_version", "dispatch"]
Loading
Loading