diff --git a/CHANGELOG.md b/CHANGELOG.md index dd8d8a5..c44d87c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,16 @@ remain historical snapshots. Beta does not mean error-free operation. ## [Unreleased] +## [0.20.1] — 2026-11-11 + +### Changed + +- Make expressive Telegram activity show the current phase, code-styled known tool + identifiers, completed/failed counts and an explicit long-wait hint. The header + indicator uses the existing heartbeat and can be disabled with + `TALOS_STATUS_ANIMATION=0`. Approval waits stop cosmetic edits; fatal errors freeze + the existing card as Stopped. Reply receipts and pending worker semantics remain. + ## [0.20.0] — 2026-11-11 ### Stable release diff --git a/CLAUDE.md b/CLAUDE.md index e49d1ac..ca0095d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 | **3492** collected tests · **277** adversarial · 44 end-to-end | +| Suites | **3499** collected tests · **277** adversarial · 44 end-to-end | | Home | · docs at `/docs/` | | Repository | `talos-kernel/talos` is the public source tree | @@ -321,7 +321,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 # 3492 collected tests; platform-specific skips are explicit +python -m pytest tests/ -q # 3499 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 diff --git a/README.md b/README.md index d4e5764..7a1e931 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,10 @@

- - Tests + Tests Red team Gate path Tools @@ -54,7 +54,7 @@ tools — but only after a deterministic permission kernel has ruled on the acti Choose **Allow once**, **Allow this task**, or an exact-action standing approval; you do not have to approve every step individually. Sandboxed shell execution and an append-only audit trail keep actions bounded and reviewable. Talos is MIT-licensed; -model-provider and hosting costs depend on your setup. The **0.20.0 stable** release retains the frozen +model-provider and hosting costs depend on your setup. The **0.20.1 stable** release retains the frozen [operator contract](docs/beta-contract.md). Stable is not a claim of error-free operation or a substitute for reviewing permissions and keeping backups. @@ -70,12 +70,13 @@ a handover, save and read back a local note, and compare release notes without i missing facts. Already tried it? [Share your first-run result](https://github.com/talos-kernel/Talos/issues/new?template=first_run.yml), including installation blockers. No star or positive review required. -**Latest signed release — [0.20.0](https://github.com/talos-kernel/Talos/releases/tag/v0.20.0):** -this stable release carries forward the qualified RC.3 runtime: safe agent migration, -Talos Connections and labelled scheduled results, including the pypdf 6.21.4 -security fix. The operator contract and permissions remain unchanged. +**Latest signed release — [0.20.1](https://github.com/talos-kernel/Talos/releases/tag/v0.20.1):** +this stable update adds clearer Telegram live activity: phase headings, code-styled +tool identifiers, measured outcomes and a quiet optional indicator. It retains safe +agent migration, Talos Connections and labelled scheduled results. The operator +contract, permissions and dependency locks remain unchanged. -**Included in 0.20.0:** migration from Hermes or +**Included in 0.20.1:** migration from Hermes or OpenClaw can now fingerprint and apply an exact redacted plan into inactive staging, archive that staging for recovery, and activate one validated imported skill at a time. [Talos Connections](docs/connections.md) adds secret-free disabled connector drafts and @@ -94,7 +95,7 @@ See the **Source versus release:** `main` can contain newer changes. Signed installers and package managers follow their pinned release; older archives remain immutable. -0.20.0 includes the Beta.8 pip 26.2.2 hash lock and isolates pip from per-user +0.20.1 includes the Beta.8 pip 26.2.2 hash lock and isolates pip from per-user configuration. When upgrading from Beta.7 or older with custom pip settings, use `PIP_CONFIG_FILE=/dev/null talos update` once; those older updaters still inherit pip configuration. @@ -107,7 +108,7 @@ configuration. - **Inspect the evidence:** review tool outcomes and the audit trail; do not mistake a confident answer for a successful action. Start with the [model, cost and permission FAQ](https://talos-agent.ch/faq/). -Talos is not Talos Linux or Cisco Talos. It is a self-hosted AI agent, with a stable 0.20.0 release. +Talos is not Talos Linux or Cisco Talos. It is a self-hosted AI agent, with a stable 0.20.1 release. If this approach is useful, **star the repository to bookmark it**, try a small task and [report a reproducible result](https://github.com/talos-kernel/Talos/issues). Want to improve it? [Fork Talos](https://github.com/talos-kernel/Talos/fork) and read @@ -330,6 +331,12 @@ compact labelled rows. Approval prompts keep the exact command visible. `/stop` interrupts the run and `/log` opens its receipts. A finished conversation does not imply that a delegated background job has finished. +Version 0.20.1 adds phase-specific headings, code-styled tool identifiers and +separate completed/failed counts to the expressive card. A quiet activity indicator +uses the existing five-second heartbeat; `TALOS_STATUS_ANIMATION=0` disables it. +Approval waits stay still, and a long wait is labelled without inventing progress +or an ETA. Tool arguments and full commands remain confined to approval prompts. + Operator-defined OpenAI-compatible endpoints can be listed in the JSON file named by `TALOS_CUSTOM_PROVIDERS` (default: `data/custom-providers.json`). Each entry needs `name`, `base_url` and `models`; optional `env_key` names that endpoint's own key. @@ -423,7 +430,7 @@ come from `stdin` instead of `getUpdates`. ``` $ talos chat - ◉ Talos / 0.20.0 + ◉ Talos / 0.20.1 Your terminal. Your rules. ────────────────────────────────────────────────────────────────────────── model claude-cli/claude-opus-4-1 diff --git a/assets/demo/README.md b/assets/demo/README.md index 33bab7b..1e5dfd4 100644 --- a/assets/demo/README.md +++ b/assets/demo/README.md @@ -10,7 +10,7 @@ DEMO_PROVIDER=claude-cli DEMO_MODEL=… assets/demo/build_gif.sh ``` `../talos-telegram-demo.gif` shows the same control model in Telegram: the live -work trail, an approval card, a reply-only receipt that names a pending worker, +work trail with phase headings, code identifiers and wait feedback, an approval card, a reply-only receipt that names a pending worker, the worker result arriving separately, and a scheduled run arriving as its own labelled notification. Rebuild it with: @@ -50,6 +50,6 @@ pauses over two seconds are capped, so model latency does not inflate the GIF. **Provenance of the shipped GIFs:** the terminal recording was rebuilt 2026-11-04 from Talos 0.20.0-rc.2 with `ollama/qwen3.8:latest` (local, no account). The -Telegram animation was generated the same day from the current source UI strings; +Telegram animation was regenerated 2026-11-11 for the 0.20.1 activity update; its transport is intentionally offline and synthetic. Rebuild either asset with the commands above. diff --git a/assets/demo/build_telegram_gif.py b/assets/demo/build_telegram_gif.py index f7bf924..5fd54c8 100644 --- a/assets/demo/build_telegram_gif.py +++ b/assets/demo/build_telegram_gif.py @@ -89,7 +89,7 @@ def plain(text: str) -> str: return html.unescape(re.sub(r"<[^>]+>", "", text)) -def talos_states() -> tuple[str, str, str, str, str]: +def talos_states() -> tuple[str, ...]: client = DemoClient() activity = TelegramActivity( client, @@ -109,16 +109,22 @@ def talos_states() -> tuple[str, str, str, str, str]: activity.progress(progress("read_file", "running", step=2, summary="read — notes.md")) client.now[0] = 3 reading = plain(client.messages[client.sent_order[0]]) + client.now[0] = 5 + activity.tick() + heartbeat = plain(client.messages[client.sent_order[0]]) + client.now[0] = 35 + activity.tick() + waiting = plain(client.messages[client.sent_order[0]]) activity.progress(progress("read_file", "done", step=2)) activity.progress( progress("write_file", "running", step=3, summary="write — summary.md") ) - client.now[0] = 7 + client.now[0] = 37 activity.progress(progress("write_file", "needs_human", step=3)) approval = plain(client.messages[client.sent_order[0]]) activity.progress(progress("write_file", "done", step=3)) activity.progress(progress("delegate_codex", "running", step=4)) - client.now[0] = 15 + client.now[0] = 45 activity.progress(progress("delegate_codex", "done", step=4)) activity.succeed() trail = plain(client.messages[client.sent_order[0]]) @@ -132,7 +138,7 @@ def talos_states() -> tuple[str, str, str, str, str]: scheduled = ScheduledRun("41c226d2", "Daily project status").wrap( "The project is healthy. The dependency scan found no known vulnerabilities." ) - return reading, approval, trail + "\n\n" + receipt, worker, scheduled + return reading, heartbeat, waiting, approval, trail, receipt, worker, scheduled def font(size: int) -> ImageFont.FreeTypeFont: @@ -220,14 +226,16 @@ def frame(messages: list[tuple[str, bool]], *, approval: bool = False, if approval: y = buttons(draw, y, selected=selected) draw.rectangle((0, HEIGHT - 72, WIDTH, HEIGHT), fill="#17212b") - draw.text((26, HEIGHT - 55), caption, font=SMALL, fill="#9fb3c8") - draw.text((WIDTH - 26, HEIGHT - 55), "simulated transport · real Talos text", + if y > HEIGHT - 84: + raise RuntimeError(f"Demo frame overflows: {caption}") + draw.text((26, HEIGHT - 65), caption, font=SMALL, fill="#9fb3c8") + draw.text((WIDTH - 26, HEIGHT - 32), "simulated transport · real Talos text", font=SMALL, fill="#6e8296", anchor="ra") return image def main() -> None: - reading, approval_text, finished, worker, scheduled = talos_states() + reading, heartbeat, waiting, approval_text, trail, receipt, worker, scheduled = talos_states() user = "Summarise notes.md, save the result, then have a worker verify it." approval_card = ( "Approval required\nwrite_file · workspace/summary.md\n" @@ -236,18 +244,21 @@ def main() -> None: frames = [ frame([(user, True)], caption="1 · request received"), frame([(user, True), (reading, False)], caption="2 · live measured work trail"), - frame([(user, True), (approval_text, False), (approval_card, False)], - approval=True, caption="3 · protected action waits for the operator"), - frame([(user, True), (approval_text, False), (approval_card, False)], - approval=True, selected=True, caption="4 · task-scoped approval selected"), + frame([(user, True), (heartbeat, False)], caption="3 · one card, current phase"), + frame([(waiting, False)], caption="4 · elapsed time is not progress"), + frame([(approval_text, False), (approval_card, False)], + approval=True, caption="5 · approval pauses the indicator"), + frame([(approval_text, False), (approval_card, False)], + approval=True, selected=True, caption="6 · task-scoped approval selected"), + frame([(trail, False)], caption="7 · completed work remains visible"), frame([("Saved the summary. The verification worker is still running.", False), - (finished, False)], caption="5 · reply receipt separates pending work"), - frame([(finished, False), (worker, False)], - caption="6 · the background result arrives separately"), + (receipt, False)], caption="8 · reply and pending work are separate"), + frame([(receipt, False), (worker, False)], + caption="9 · the worker result follows separately"), frame([(scheduled, False)], - caption="7 · a scheduled result is one labelled notification"), + caption="10 · scheduled results keep their own label"), ] - durations = [1400, 1800, 2200, 1100, 2600, 2800, 3200] + durations = [1400, 1800, 1800, 2400, 2200, 1100, 2200, 2600, 2800, 3200] frames[0].save( OUT, save_all=True, diff --git a/assets/talos-telegram-demo.gif b/assets/talos-telegram-demo.gif index daee8a3..01f6d2b 100644 Binary files a/assets/talos-telegram-demo.gif and b/assets/talos-telegram-demo.gif differ diff --git a/docs/stable-readiness.md b/docs/stable-readiness.md index 5f9aa73..1612ccf 100644 --- a/docs/stable-readiness.md +++ b/docs/stable-readiness.md @@ -95,4 +95,14 @@ installation or restart its service. | `0.20.0-rc.1` | Published, superseded candidate | Original signed assets remain available. Its observation days do not qualify RC.3. | | `0.20.0-rc.2` | Published, superseded candidate | Original signed security release remains available. | | `0.20.0-rc.3` | Published upgrade baseline | Signed candidate, with seven deployed daily records dated 5–11 November 2026. Transient channel/provider errors were reviewed; no unresolved security or data-loss regression was identified. | -| `0.20.0` | Stable source | Release publication requires the final-artifact gates above; matching signed assets and hosted CI establish delivery. No new runtime feature is included. | +| `0.20.0` | Published stable baseline | Signed artifacts and [main CI](https://github.com/talos-kernel/Talos/actions/runs/42986282432) qualify the first stable release. Seven RC.3 calendar dates and final-artifact install/upgrade/rollback checks passed. | + + +## Subsequent stable update: 0.20.1 + +Version 0.20.1 adds the separately qualified Telegram activity update after the first +stable release. It retains the operator contract, permissions and dependency locks. +Qualify its exact source and signed bytes with the source, platform, real-model, +installation/recovery and delivery gates above. Use `--previous 0.20.0 --current +0.20.1`; do not relabel the historical RC.3 observations as observations of this update. +Publication is established by matching signed assets and CI, not this source label. diff --git a/site/console.html b/site/console.html index 81066b5..565c56d 100644 --- a/site/console.html +++ b/site/console.html @@ -365,7 +365,7 @@

-

talos 0.20.0 · operator console · MIT

+

talos 0.20.1 · operator console · MIT

recorded · unfurled · no live wire

Witness one deed from door to ledger.

This hall unfurls one true session, recorded on the working machine @@ -378,7 +378,7 @@

Witness one deed from door to ledger.

This candidate, measured
- 3492automated tests + 3499automated tests 277/277adversarial 996lines · gate 37gated tools @@ -553,12 +553,12 @@

Numbers this candidate must ship.

embedded below. Either way the digits come from a count, not from memory.

-
0.20.0
stable release
public installer v0.20.0
-
3492
automated tests, collected by pytest
+
0.20.1
stable release
public installer v0.20.1
+
3499
automated tests, collected by pytest
277
adversarial cases, all behaving
996
lines in policy.py, readable in one sitting
37
tools, every one through the gate
-
v0.20.0
release tag
+
v0.20.1
release tag
diff --git a/site/docs/index.html b/site/docs/index.html index e71a1ef..0756e23 100644 --- a/site/docs/index.html +++ b/site/docs/index.html @@ -244,7 +244,7 @@

Talos AI agent: the field manual

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.

-
3492automated tests
+
3499automated tests
277/277adversarial
996gate lines
37gated tools
@@ -378,10 +378,10 @@

Section 03Give it a mind

-

0.20.0Move from Hermes or OpenClaw

+

0.20.1Move from Hermes or OpenClaw

- Included in the signed 0.20.0 installer -

Upgrade to 0.20.0 to use these commands. Preview the fingerprinted plan before + Included in the signed 0.20.1 installer +

Upgrade to 0.20.1 to use these commands. Preview the fingerprinted plan before applying it; staged content grants no permissions.

Preview first. Talos inventories only portable profile text, memory documents @@ -436,7 +436,7 @@

Section 04Run it

Section 05The terminal session

$ talos chat
-Talos 0.20.0  ·  anthropic-api/claude-opus-4-8  ·  autonomy 3
+Talos 0.20.1  ·  anthropic-api/claude-opus-4-8  ·  autonomy 3
 speaking as cli:1000  ·  approvals possible  ·  you are at a terminal
 /help for commands, `exit` to leave
 
@@ -688,7 +688,7 @@ 

Section 11Channels

- + diff --git a/site/faq/index.html b/site/faq/index.html index 481ed11..3ee6da9 100644 --- a/site/faq/index.html +++ b/site/faq/index.html @@ -47,8 +47,8 @@

Getting started

How do I install Talos on macOS or Linux?

Follow the installation guide: inspect the Bash installer, install the signed release, configure your identity and model, then start terminal chat. Talos requires Python 3.11 or newer. The installer verifies the release and runs its tests; it does not start the agent automatically. A separate Pinokio launcher is also available; check the release it currently pins.

Can Talos run on a Raspberry Pi or a VPS?

Yes. Talos runs on Linux, including Raspberry Pi, as well as macOS. The agent and the model are separate: a Pi can run Talos while a configured provider supplies inference. Running a large model locally needs enough memory and compute for that model. Self-hosting does not imply that every model fits on a Pi. See model setup.

Can I use Talos as a self-hosted Telegram AI agent?

Yes. Configure your own Telegram bot and explicitly allow your identity. Talos fetches messages by long polling; the Telegram channel does not need a public webhook. Follow the Telegram setup instructions. Keep one service polling each bot token to avoid competing consumers.

-
Can I migrate from Hermes or OpenClaw?

Talos 0.20.0 can preview a redacted, fingerprinted plan for portable profile text, memory documents and Agent Skills, then apply exactly that plan into inactive staging. It does not import passwords, permissions, schedules, plugins, sessions, databases or live connector access. This feature is included in 0.20.0; see the migration guide.

-
What are Talos Connections?

Talos Connections is the public source interface for the existing isolated-worker registry and secret-free disabled connector drafts. A draft is inert: the operator must separately install the server and opt in through both agent and worker allowlists. It contains no private Atlas service, bundled account, endpoint or credential. Connections is included in the signed 0.20.0 installer. See the guide and commands.

+
Can I migrate from Hermes or OpenClaw?

Talos 0.20.1 can preview a redacted, fingerprinted plan for portable profile text, memory documents and Agent Skills, then apply exactly that plan into inactive staging. It does not import passwords, permissions, schedules, plugins, sessions, databases or live connector access. This feature is included in 0.20.1; see the migration guide.

+
What are Talos Connections?

Talos Connections is the public source interface for the existing isolated-worker registry and secret-free disabled connector drafts. A draft is inert: the operator must separately install the server and opt in through both agent and worker allowlists. It contains no private Atlas service, bundled account, endpoint or credential. Connections is included in the signed 0.20.1 installer. See the guide and commands.

Your models. Your costs.

@@ -68,7 +68,7 @@

The terms of the grant

Judge the work

How should I compare Talos with IronClaw, OpenClaw or Hermes?

Compare the same task, model, host resources and permission scope. Measure completed outcomes, operator effort, latency and recovery, not just stars or test counts. Talos focuses on a readable deterministic capability kernel and scoped approvals; other projects may suit different integrations or workflows. We do not claim a universal winner. Read the documented comparison and check each project's current source for its own capabilities.

-
Is Talos production-ready?

Talos 0.20.0 is stable under its documented operator contract. Automated tests and signed releases are evidence for specific checks, not a guarantee of error-free operation. Review the frozen operator contract, validate your own workflows and keep a rollback path. Experimental Computer adapters have separate qualification requirements; a source implementation is not a ready-to-use VM.

+
Is Talos production-ready?

Talos 0.20.1 is stable under its documented operator contract. Automated tests and signed releases are evidence for specific checks, not a guarantee of error-free operation. Review the frozen operator contract, validate your own workflows and keep a rollback path. Experimental Computer adapters have separate qualification requirements; a source implementation is not a ready-to-use VM.

How can I star, fork or contribute to Talos?

Visit Talos on GitHub and star it if you want to keep track of the project. Fork the repository to experiment, follow the contribution guide, or open a reproducible issue. Helpful contributions include installation reports, clearer documentation and regression tests. Share only sanitised examples, never tokens, private logs or personal configuration.

These answers describe Talos, not a hosted service. Check the release notes for the version you install. A GitHub star is a bookmark and a signal of interest, not a security certificate.

diff --git a/site/index.html b/site/index.html index d299320..f070d15 100644 --- a/site/index.html +++ b/site/index.html @@ -530,15 +530,18 @@

Give it work worthy of a sworn hand.

4 channels

Terminal, Telegram, mail over IMAP, WhatsApp through an operator-controlled broker over SSH. Every way in fetches; nothing opens a listening socket.

+
live activity

Telegram can show the current phase, + code-styled tool names and completed or failed counts in one card. The optional + indicator pauses for approval; a waiting hint never invents progress or an ETA.

-

new in 0.20.0

+

included in 0.20.1

Move the work. Leave authority behind.

-

The 0.20.0 installer can prepare a bounded move from Hermes or OpenClaw and can record +

The 0.20.1 installer can prepare a bounded move from Hermes or OpenClaw and can record disabled connector drafts. It never inherits an old agent's passwords, grants, schedules or live connections.

@@ -557,7 +560,7 @@

Move the work. Leave authority behind.

current recordings in the GitHub README. Their provenance states which parts are real and which transport is simulated.

-

Included in 0.20.0. Review the migration fingerprint before applying; imported +

Included in 0.20.1. Review the migration fingerprint before applying; imported content stays inactive until you explicitly activate a validated skill.

@@ -687,7 +690,7 @@

Measured, with the path stated

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.

Stable source and signed release. Counts describe the source test suite, not speed or real-world success rates; signed release contents follow their tag.

-
0
automated tests collected from the source tree
+
0
automated tests collected from the source tree
0
adversarial cases trying to pass the kernel
0
opt-in end-to-end cases against a real model
0
lines in the file that decides every verdict
@@ -812,7 +815,7 @@

Talos AI agent: your questions

raise the keep

The command remains yours

-

Stable 0.20.0. Tested on Python 3.11 · 3.13, Linux and macOS, including Raspberry Pi. +

Stable 0.20.1. Tested on Python 3.11 · 3.13, Linux and macOS, including Raspberry Pi. Bring your own model: a supported CLI, configured API provider or local Ollama. The installer runs the unit and adversarial suites in front of you, then starts nothing.

diff --git a/site/install.sh b/site/install.sh index 327e86f..21c5ca0 100755 --- a/site/install.sh +++ b/site/install.sh @@ -15,7 +15,7 @@ set -euo pipefail -VERSION="0.20.0" +VERSION="0.20.1" BASE="${TALOS_BASE:-https://talos-agent.ch}" TARBALL="${BASE}/dist/talos-${VERSION}.tar.gz" PREFIX="${TALOS_PREFIX:-$HOME/talos}" diff --git a/site/llms.txt b/site/llms.txt index bc53143..74c3057 100644 --- a/site/llms.txt +++ b/site/llms.txt @@ -6,13 +6,13 @@ - Permission kernel: 996 lines of Python, the only code that decides - 37 tools, all gated; unattended runs cannot escalate -- 3492 tests collected in the source tree; 277/277 red-team cases passed in source verification. Release contents follow their own signed tag. +- 3499 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 -- Latest signed release: 0.20.0. Includes safe migration, Talos Connections and labelled scheduled-result notifications. -- Migration in 0.20.0: fingerprinted redacted preview, inactive staging apply, recovery archive and one-at-a-time validated skill activation; no inherited secrets or authority -- Talos Connections in 0.20.0: isolated-worker inventory plus disabled secret-free drafts; no bundled account, endpoint or private service +- Latest signed release: 0.20.1. Adds expressive Telegram phase headings, code-styled tool identifiers, measured outcome counts and an optional activity indicator; retains migration, Connections and scheduled results. +- Migration in 0.20.1: fingerprinted redacted preview, inactive staging apply, recovery archive and one-at-a-time validated skill activation; no inherited secrets or authority +- Talos Connections in 0.20.1: isolated-worker inventory plus disabled secret-free drafts; no bundled account, endpoint or private service - Scheduled runs on current source stay quiet while working and deliver one labelled final result; interactive work trails show bounded skill and executable names without arguments or contents - License: MIT diff --git a/site/redteam/index.html b/site/redteam/index.html index 44e3913..827bddd 100644 --- a/site/redteam/index.html +++ b/site/redteam/index.html @@ -224,7 +224,7 @@

277 ways to break an agent.

25attack surfaces
996kernel lines
37gated tools
-
3492automated tests
+
3499automated tests
0passes sold as safety
@@ -394,7 +394,7 @@

Section 06Attack it yourself

Roll of Arms Weigh the Orders Source - MIT · REV 0.20.0 + MIT · REV 0.20.1
diff --git a/site/registry/index.html b/site/registry/index.html index 8a5f91e..09abad2 100644 --- a/site/registry/index.html +++ b/site/registry/index.html @@ -239,7 +239,7 @@

The MCP registry

guide with the full protocol is docs/claude-worker.md. A curated starting point ships as examples/mcp-servers.json.

-
3492automated tests
+
3499automated tests
277/277adversarial
996gate lines
37gated tools
diff --git a/site/setup/index.html b/site/setup/index.html index f95beeb..0d7bcad 100644 --- a/site/setup/index.html +++ b/site/setup/index.html @@ -163,7 +163,7 @@

This address moved.

Return to the keep
-
3492automated tests
+
3499automated tests
277/277adversarial
996gate lines
37gated tools
diff --git a/site/status.json b/site/status.json index bc60ae1..96455c6 100644 --- a/site/status.json +++ b/site/status.json @@ -1,11 +1,11 @@ { - "version": "0.20.0", + "version": "0.20.1", "release_state": "released", "source_state": "release", - "installer_version": "0.20.0", - "tests": 3492, + "installer_version": "0.20.1", + "tests": 3499, "redteam_faelle": 277, "kernel_zeilen": 996, "werkzeuge": 37, - "release_ref": "v0.20.0" + "release_ref": "v0.20.1" } diff --git a/site/vergleich/index.html b/site/vergleich/index.html index 3262543..fc28db2 100644 --- a/site/vergleich/index.html +++ b/site/vergleich/index.html @@ -290,7 +290,7 @@

The others can do more.

37tools here, every one gated
100+skills there · counted honestly
-
3492automated tests
+
3499automated tests
277/277adversarial cases
996gate lines
0inbound ports
diff --git a/talos/__init__.py b/talos/__init__.py index c577c71..8f82f90 100644 --- a/talos/__init__.py +++ b/talos/__init__.py @@ -8,4 +8,4 @@ advertised and a released package is never left behind. """ -__version__ = "0.20.0" +__version__ = "0.20.1" diff --git a/talos/schema.py b/talos/schema.py index 3316685..72230ee 100644 --- a/talos/schema.py +++ b/talos/schema.py @@ -241,6 +241,9 @@ def _model_overrides(value: str) -> str: Key("TALOS_STATUS_STYLE", SETTING, "activity display: geometric symbols or expressive emoji and a timeline", default="geometric", validate=_status_style), + Key("TALOS_STATUS_ANIMATION", SETTING, + "expressive Telegram activity indicator: 1 enabled, 0 reduced motion", + default="1", validate=_bool01), Key("TELEGRAM_BOT_USERNAME", SETTING, "shown in the greeting; purely cosmetic", validate=_one_line), Key("TALOS_MAIL_HOST", SETTING, "IMAP host of the mail channel", validate=_one_line), diff --git a/talos/telegram.py b/talos/telegram.py index b76070f..a6cf2af 100644 --- a/talos/telegram.py +++ b/talos/telegram.py @@ -779,12 +779,14 @@ class _Line: text: str started: float done_at: float | None = None + tool_id: str = "" - def render(self, now: float) -> str: + def render(self, now: float, *, identify_tool: bool = False) -> str: span = (self.done_at if self.done_at is not None else now) - self.started # Unter einer Sekunde keine Zahl: sonst zappelt die Zeile bei jedem Edit. stamp = f" {span:.0f}s" if span >= 1.0 else "" - return f"{self.sym} {self.text}{stamp}" + identity = f" · [{self.tool_id}]" if identify_tool and self.tool_id else "" + return f"{self.sym} {self.text}{identity}{stamp}" def _keep_work_trail() -> bool: @@ -837,6 +839,9 @@ def __init__( ) -> None: self._client = client self._style = style + # Only the header changes rhythm; reading content never moves. Telegram + # has no reduced-motion signal, so operators can explicitly disable this. + self._animate = os.environ.get("TALOS_STATUS_ANIMATION", "1").strip() == "1" self._chat_id = chat_id self._conversation = conversation self._background_jobs = background_jobs @@ -861,6 +866,7 @@ def __init__( self._finished = False self._start = clock() self._last_update = self._start + self._last_progress = self._start # Serialize updates with terminal delivery: no heartbeat after the final state. self._lock = threading.RLock() # Noch KEINE Nachricht: erst ein Werkzeug rechtfertigt eine (siehe `_ensure_message`). @@ -916,6 +922,7 @@ def progress(self, event: AgentProgress) -> None: def _progress(self, event: AgentProgress) -> None: if self._finished: return + self._last_progress = self._clock() self._current_stage = event.stage if event.stage is ProgressStage.TOOL: self._current_tool = event.tool @@ -952,6 +959,7 @@ def _progress(self, event: AgentProgress) -> None: self._style.tool_symbol(event.tool), _tool_text(event, self._style), self._clock(), + tool_id=event.tool if event.tool in TOOL_LABELS else "", ) ) # Hier faellt die Entscheidung: ab jetzt gibt es etwas zu belegen. @@ -968,6 +976,8 @@ def tick(self) -> None: with self._lock: if self._finished: return + if self._waiting: + return # Approval is a resting state, not continuing execution. self._send_update() if self._message_id is None: self._typing() @@ -1109,6 +1119,9 @@ def _fail(self, error: str) -> None: except Exception: pass if self.failure_delivered: + # Stop the visible live state as well as its timer. The separate + # failure message carries the error; this remains the work evidence. + self._edit(text=self._render(final=True), force=True) return # Preserve the last visible evidence when even the failure cannot be sent. body = self._render(final=True) @@ -1179,6 +1192,8 @@ def _close_last(self, event: AgentProgress) -> None: line.sym, line.text = self._style.blocked, f"{_tool_text(event, self._style)} — {TXT_REFUSED}" else: line.sym = self._style.fail + if self._style is EXPRESSIVE: + line.text += " — failed" def _settle(self) -> None: self._end_thinking() @@ -1218,7 +1233,16 @@ def _mission(self, now: float, elapsed: float, *, final: bool, footer: str, bele elif final: glyph, state = ("⚠️", "Reply done with issues") if self._issues else ("🏁", "Reply done") else: - glyph, state = self._style.talos, "Working" + glyph = self._style.talos + if self._current_stage is ProgressStage.TOOL: + state = self._style.tool_label(self._current_tool, "Running a tool") + elif self._current_stage is ProgressStage.THINKING: + state = "Thinking" + else: + state = "Continuing" + if self._animate and not self._waiting: + # Reuse the existing five-second heartbeat, no extra timer/API calls. + state += " " + "." * (int(max(0, elapsed) // DEFAULT_HEARTBEAT_S) % 3 + 1) seconds = max(0, int(elapsed)) duration = f"{seconds // 60}m {seconds % 60:02d}s" if seconds >= 60 else f"{seconds}s" calls = f"{self._tool_calls} tool call{'s' if self._tool_calls != 1 else ''}" @@ -1229,10 +1253,14 @@ def _mission(self, now: float, elapsed: float, *, final: bool, footer: str, bele if not final and self._step: # The safety budget is neither task size nor a useful completion estimate. parts[-1] += f" · Step {self._step}" + if not final and self._tool_calls: + parts.append(f"{self._completed_calls} completed · {self._issues} failed or refused") parts.append("") if self._dropped: parts.append(f"↥ {self._dropped} earlier events · /log for the full record") - parts.extend(line.render(now) for line in self._lines) + parts.extend(line.render(now, identify_tool=True) for line in self._lines) + if not final and not self._waiting and now - self._last_progress >= 30: + parts.append("\nWaiting for the next result · elapsed time is not progress") if self._issues: parts.append(f"\n⚠️ {self._issues} tool call{'s' if self._issues != 1 else ''} refused or failed") if final and footer and not beleg: @@ -1248,6 +1276,9 @@ def _format(self, text: str) -> str: lines[0] = f"{html.escape(lines[0])}" for index in range(1, len(lines)): line = html.escape(lines[index]) + # Only fixed tool identifiers receive code styling; never arguments. + line = re.sub(r"\[([A-Za-z0-9_]+)\]", + lambda m: f"{m[1]}" if m[1] in TOOL_LABELS else m[0], line) # Quiet metadata under a clear heading; every dynamic value stays escaped. lines[index] = f"{line}" if index == 1 else line return "\n".join(lines) diff --git a/tests/test_telegram_live_tracking.py b/tests/test_telegram_live_tracking.py new file mode 100644 index 0000000..1dc6755 --- /dev/null +++ b/tests/test_telegram_live_tracking.py @@ -0,0 +1,95 @@ +"""Telegram activity communicates measured states without exposing tool arguments.""" +from talos.agent_loop import AgentProgress, ProgressStage +from test_telegram_progress_updates import setup_activity +from test_telegram_ux import _tool, _result + + +def test_live_phase_indicator_changes_only_the_header_and_uses_existing_tick(): + client, activity = setup_activity() + activity.progress(_tool('run_shell', summary='shell — pytest')) + initial = client.sent[0][1] + assert 'Running .' in initial + assert 'run_shell' in initial and 'Running pytest' in initial + client.now[0] = 5 + activity.tick() + assert 'Running ..' in client.edited[-1][2] + assert len(client.sent) == 1 + activity.progress(_result('run_shell', 'done')) + client.now[0] = 10 + activity.progress(AgentProgress(ProgressStage.THINKING, step=2)) + assert 'Thinking ...' in client.edited[-1][2] + assert '1 completed · 0 failed or refused' in client.edited[-1][2] + activity.succeed() + + +def test_reduced_motion_disables_the_indicator_but_keeps_measured_time(monkeypatch): + monkeypatch.setenv('TALOS_STATUS_ANIMATION', '0') + client, activity = setup_activity() + activity.progress(_tool('read_file', summary='read — notes.md')) + before = client.sent[0][1].splitlines()[0] + client.now[0] = 15 + activity.tick() + assert client.edited[-1][2].splitlines()[0] == before + assert '15s' in client.edited[-1][2] + activity.succeed() + + +def test_approval_parks_live_edits_and_resumes_on_a_real_result(): + client, activity = setup_activity() + activity.progress(_tool('write_file', summary='write — notes.md')) + activity.progress(_result('write_file', 'needs_human')) + before = len(client.edited), len(client.sent) + client.now[0] = 90 + activity.tick() + assert (len(client.edited), len(client.sent)) == before + assert 'Needs your approval' in client.edited[-1][2] + activity.progress(_result('write_file', 'done')) + assert '1 completed' in client.edited[-1][2] + activity.succeed() + + +def test_waiting_hint_is_based_on_events_and_clears_when_work_returns(): + client, activity = setup_activity() + activity.progress(_tool('run_shell', summary='shell — pytest')) + client.now[0] = 30 + activity.tick() + assert 'Waiting for the next result' in client.edited[-1][2] + assert '0 completed' in client.edited[-1][2] + activity.progress(_result('run_shell', 'done')) + assert 'Waiting for the next result' not in client.edited[-1][2] + assert '1 completed' in client.edited[-1][2] + activity.succeed() + + +def test_code_labels_never_reveal_commands_and_dynamic_html_is_escaped(): + client, activity = setup_activity(name='Example') + activity.progress(_tool('run_shell', summary='curl --password PRIVATE_CANARY')) + activity.progress(_tool('read_file', summary='read — ')) + rendered = client.edited[-1][2] + assert 'PRIVATE_CANARY' not in rendered and 'curl' not in rendered + assert '
ChannelTrustNotes
telegramFULLButtons for approvals, live status, images in.
telegramFULLApproval buttons, phase headings, tool outcomes and images. Set TALOS_STATUS_STYLE=expressive for the live work card; TALOS_STATUS_ANIMATION=0 disables its indicator.
cliFULLchat and ask. Approvals only at a terminal.
mailASKSender proven from the receiving server's Authentication-Results, fail-closed.
whatsappFULL · NOTIFYBroker over SSH pull (FULL) · or Cloud API, outbound only (NOTIFY).