diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e30fa43..af3ef125 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,115 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [2026.8.6] - 2026-08-06 + +### Added + +- A cron job can run a script instead of a model turn. `--job-kind script` + makes the script the job: its stdout is delivered verbatim, empty stdout is a + silent tick, and a non-zero exit delivers the error and fails the job so a + broken watchdog cannot be mistaken for a quiet one. `--job-kind agent_turn + --script` runs the script first as a collector — its stdout is prepended to + the prompt as a `## Script output` block, and a tick that prints nothing (or + ends with `{"wakeAgent": false}`) skips the turn before the session is + touched, leaving no session row, transcript line, or model call behind. + Scripts resolve inside `~/.agentos/scripts/`; absolute paths, `~`, `..`, and + symlinks out of it are refused, and arguments are exec'd as argv, never handed + to a shell. Scheduling one requires an interactive CLI or Web caller — the + in-agent `cron` tool refuses it from a chat channel. (Refs #219) +- New bundled skill `cron-watchers` ships the three script jobs everyone writes + first — an RSS/Atom feed, a JSON endpoint, and a GitHub repo — each following + the contract the scheduler expects: print what is new, print nothing when + nothing is new, exit non-zero on a real failure. Deduplication state lives in + `~/.agentos/state/cron-watchers/.json`, outside the scripts directory, + and the first run reports nothing by default. (Refs #219) +- `agentos cron output [--run ]` and the `cron.runOutput` RPC + read one run's output in full; the in-agent `cron` tool gains + `action="runs"`, so "what did the watcher report?" is a question the model can + look up instead of invent. +- `senior-unilp-manager` can open the pool a position lives in. + `lp_write.py create-pool` initializes hook-less Uniswap v4 pools — + `hooks` is pinned to `address(0)` with no flag to change it, a dynamic fee is + refused, an odd fee/tick-spacing pair needs `--allow-odd-tier` because `pools` + only searches the vanilla tiers, and an already-initialized pool prints its + poolId and exits without planning. The starting price can never be corrected + afterwards, so the plan table prints tick, `sqrtPriceX96`, and the price in + both directions under a banner saying so. selftest goes 695 → 716 assertions. +- `tick --json --alert-only` lets a `senior-unilp-manager` ratchet cron stay + quiet. A tick that found nothing still prints the whole payload — the run + history keeps it — but ends on `{"wakeAgent": false}`, which + `has_actionable_output()` reads as "no news": the run succeeds and nothing is + delivered. A tick that fired, adopted a landed fire, halted, was rejected, + expired, or built a plan on a dry run is delivered as usual, and + `NEEDS_ATTENTION` deliberately alerts on every tick — it is a terminal state, + so filtering on the action alone would silence the one alarm that must never + go quiet. (Closes #234) +- `lp_read.py price --tokens` is documented (SKILL.md §6b), with the rule to use + it rather than deriving a price from a pool — a session derived a token's + price from a zero-TVL dust pool, was off by 3×, and that number would have + become the permanent starting tick of a new pool. (Refs #228) + +### Fixed + +- A bad cron delivery target is rejected at save time instead of failing every + run. `validate_channel_target` refuses an id beginning with a session-key + prefix (`agent:`, `cron:`, `webchat:`, `session:`) and requires an integer or + `@username` for Telegram, suggesting the id that would have worked; `cron.add` + and `cron.update` then ask the adapter itself via `TelegramChannel.probe_target`, + where only a definite "no" blocks the save. The new `channels.deliveryTargets` + RPC lists each channel's paired DMs and configured group chats, so the Web UI + renders Recipient as a dropdown with `Enter manually…` as the escape hatch. +- A cron run record now carries *why* delivery failed. `DeliveryReport.channel_detail` + turns one line of "delivery failed" into "delivery to telegram failed: Bad + Request: chat not found" in `agentos cron runs`, and an exception escaping the + channel leg reports its type instead of vanishing into `asyncio.gather`. +- `structlog` events reach `~/.agentos/logs/debug.log`. Half this codebase logs + through `logging.getLogger` and half through structlog, and only the first half + was written to the file — the missing half included every `delivery.*` warning. +- A cron run's output is stored whole. `clamp_run_output` replaces the scattered + `[:500]` slices that truncated a script job's stdout on the way into the + database; `preview_summary` is what the delivery layer and the run list get, + and the Web UI's expanded row fetches the full text lazily. +- The "→ Chat" button no longer leads to "Could not load chat history." Each run + reports `chatAvailable` and the button is hidden when it is false — script jobs + never create a session, and isolated agent sessions are reaped after 24h — while + `chat.history` answers an empty transcript for a missing cron session instead of + raising. +- The session reaper is paged. `list_sessions()` returns the 100 most recently + updated sessions — precisely the ones that are *not* expired — so expired + isolated cron sessions were never reaped on a busy store. +- A script job's output no longer vanishes. Several skips in the delivery chain + encode "the run already wrote this into the session", which holds for an agent + turn but not for a script, which has no turn: a job with `sessionTarget=current` + from webchat was reported delivered without a byte being written, and a job + bound to the chat its run *is* was skipped on `origin == session_key`. A + `cron add --script` from the CLI, which genuinely has nowhere to write, now + reports `no_session_target` rather than a bare `skipped`. +- A cron tool refusal reaches the model. Cron raises plain `ToolError` in ~30 + places with field-naming messages that `envelope.py` discarded, so a call + carrying `tool_policy.elevated` on a script job came back as "received an + invalid argument" followed by seven retries that dropped the required + `schedule` field. Cron's refusals are `SafeToolError` now, and `job_kind='script'` + + `tool_policy.elevated` is rejected up front, naming the field. (Refs #228) +- A quoted script path is unwrapped before it is stored. A model passes + `script='"watch-memory.sh"'` often enough that it is the first thing that + happens; the job saved cleanly and failed on its first tick. (Refs #219) +- `/reset` clears the visible conversation on web and CLI. `sessions.reset` keeps + the session key and only rotates `session_id`, so the transcript on screen + stayed put, which reads as "nothing happened". The Web UI clears on + `session.epoch_changed`, which covers the typed `/reset`, the slash menu, the + SessionChip button, and a reset issued by another client; the CLI gains + `ChatApplication.clear_screen()`, which drops scrollback too. +- Two `senior-unilp-manager` doc commands were unrunnable — `python3 /ratchet.py` + reads as a redirect from a file named `S` in a shell — and the cron examples + cannot use `$S` at all, since a cron job runs in a fresh isolated session. Both + now spell out `{baseDir}/scripts/ratchet.py`. (Refs #228) + +### Changed + +- The cron surfaces say "no LLM" instead of "no model", and `agentos cron runs` + grows Delivery and Output columns. + ## [2026.8.5] - 2026-08-05 ### Added diff --git a/README.md b/README.md index 3189122b..dec9475f 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ OpenAI, Anthropic, Ollama, DeepSeek, Gemini, Qwen/DashScope, and 20+ other providers. You do not need to change your code or config to switch providers. -AgentOS 2026.8.5 is the current release. The project website is +AgentOS 2026.8.6 is the current release. The project website is [useagentos.dev](https://useagentos.dev). Follow [@useAgentOS](https://x.com/useAgentOS) on X for updates. @@ -224,14 +224,14 @@ agentos gateway run > new terminal window. Or run the PATH command from step 1 again. For an install pinned to one exact version, add `==` — for -example `uv tool install --python 3.12 "use-agent-os[recommended]==2026.8.5"` — +example `uv tool install --python 3.12 "use-agent-os[recommended]==2026.8.6"` — or use the GitHub release wheel link directly: -`https://github.com/use-agent-os/agent-os/releases/download/v2026.8.5/use_agent_os-2026.8.5-py3-none-any.whl`. +`https://github.com/use-agent-os/agent-os/releases/download/v2026.8.6/use_agent_os-2026.8.6-py3-none-any.whl`. > [!NOTE] > Release install commands use published GitHub release assets. > Python wheel installs use versioned wheel filenames — for example -> `use_agent_os-2026.8.5-py3-none-any.whl` — because the installers validate the +> `use_agent_os-2026.8.6-py3-none-any.whl` — because the installers validate the > version segment inside the wheel filename, so there is no `latest` > wheel alias. Only the Windows portable zip has a version-independent > `releases/latest/download/` alias. diff --git a/RELEASES.md b/RELEASES.md index c9a74462..58babd8e 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -2,6 +2,7 @@ | Version | Tag | Date | Notes | |---|---|---|---| +| 2026.8.6 | v2026.8.6 | 2026-08-06 | A cron release: schedules can now run scripts, and the run history is finally readable. `--job-kind script` makes the script the job — stdout delivered verbatim, empty stdout a silent tick, non-zero exit a failed job — while `--job-kind agent_turn --script` runs the script first as a collector and skips the turn entirely when it prints nothing or ends with `{"wakeAgent": false}`, before the session is touched. Scripts resolve inside `~/.agentos/scripts/` with no escape via absolute paths, `~`, `..`, or symlinks, and are exec'd as argv. The new bundled `cron-watchers` skill ships the three watchers everyone writes first (RSS/Atom, JSON endpoint, GitHub repo). Around that, the surface that reports what a job did was rebuilt: run output is stored whole instead of clipped at 500 chars (`agentos cron output`, `cron.runOutput`, and lazy fetch in the Web UI), a script job's output no longer vanishes into delivery skips written for agent turns, the "→ Chat" button is hidden when no chat exists rather than leading to "Could not load chat history", and the session reaper stops missing expired sessions on a busy store because `list_sessions()` only returned the 100 most recent. A bad delivery target is refused at save time — a session key is not a Telegram chat id — with a dropdown of paired chats offered instead of free text, the failure reason lands on the run record, and `structlog` events (every `delivery.*` warning among them) finally reach `debug.log`. Cron's tool refusals reach the model as messages it can self-correct from instead of "received an invalid argument". `/reset` now clears the conversation on screen on both web and CLI. `senior-unilp-manager` gains `create-pool` for hook-less v4 pools — hooks pinned to `address(0)`, dynamic fees refused, odd tiers gated, and the uncorrectable starting price printed three ways before confirm — plus `tick --alert-only`, which lets a healthy ratchet cron stay quiet without silencing `NEEDS_ATTENTION`. | | 2026.8.5 | v2026.8.5 | 2026-08-05 | A cron-reliability and web-UI release. A cron job no longer fails forever once the chat it was created from is gone: the web UI stamps `originSessionKey` onto every reminder job while forcing its target to `isolated`, and "New Chat" mints a session key client-side with no row, so a reminder created before the first message pointed at a session that never existed — the fire-time mirror then raised `KeyError: Session not found`, surfaced as `forward_failed`, and failed the run with no `best_effort` checkbox rendered for the `none` delivery mode it ran under. The mirror is opportunistic now and reports a distinct `origin_gone` status; genuine channel delivery failures still fail the run. Unknown cron tool profiles are rejected at write time instead of dying ~50 ms into every firing until the scheduler auto-paused the job — `normalize_tool_profile` raises listing the profiles that exist, the read direction stays tolerant so already-broken rows can still be listed and deleted, and the tool schema now names the valid profiles. In the web UI, cron cards stop spilling over the neighbouring column (an unbreakable session key has no break opportunity per UAX-14, and three boxes between the grid track and the text could not shrink), and a job's ID — the handle every `agentos cron …` command takes — is finally visible, shortened with a copy button. The Skills page gains `Use` / `Use in chat` buttons that pre-fill the chat composer with `use skill ` instead of making the operator retype it. `senior-unilp-manager` can arm an unattended take-profit ratchet on a one-sided Uniswap v4 position: at milestones measured against the original principal it exits, keeps the converted side as realized profit, and redeploys the remainder into a narrower range in a single `modifyLiquidities` (DECREASE → BURN → MINT → TAKE_PAIR, no SETTLE), authorized by an object the CLI structurally cannot construct. One live mandate per position is now enforced by scan, since `mandate_id` hashes the label and two labels armed two mandates to burn the same NFT. The combined unlock has not been rehearsed against a hooked pool; SKILL.md requires a dust rehearsal first. | | 2026.8.3 | v2026.8.3 | 2026-08-03 | A packaging, skills, and upgrade-path release. `agentos upgrade` now installs the published release instead of delegating to `uv tool upgrade`, which re-resolved uv's *directory* receipt on checkout-backed installs and re-packaged whatever `static/dist/` was on disk — so Python moved forward while the web UI silently did not; installing a checkout stays with `scripts/install_source.sh`, the only path that rebuilds the bundle first. A `pip install use-agent-os` no longer resolves open-ended: bounds now cover the rest of the base runtime and the consumer-facing extras, each cap at the first release its upstream may break in — the next major for a `>=1.0` project, the next **minor** for a `0.x` one — recomputed from `uv.lock` by `tests/test_packaging/test_pyproject_invariants.py` so a new dependency cannot ship unbounded by accident (#153). Shipped Pilot Router tier defaults resolved against static tables with no entry for them and both tables fail open without logging, so `glm-4.7-flashx` estimated at a generic $3/$15 and seven ids — including `anthropic/claude-opus-5` — sized turns against generic limits; every tier default now carries an explicit pricing and catalog entry (#139). OpenCAP cost estimates no longer fall back to Bankr's rate sheet for the life of a process after one failed boot fetch — the price cache refreshes on a TTL, and a static-table estimate is logged once per model. Seven GMGN trading skills ship bundled under the new **Trading** category, driving the third-party `gmgn-cli` that AgentOS does not redistribute. A hub install can no longer silently shadow a bundled skill, `skill_search_community` answers with an `installed_match` block naming what a local skill is missing, and `skill_view(name="agentos", section="Skills")` resolves. Durable memory redaction moves onto the shared scanner (forced, since `AGENTOS_REDACT_SECRETS=0` is an egress-only escape hatch), which also learned the `ASIA`/`ABIA`/`ACCA` AWS prefixes and quoted `Authorization`/`x-api-key` headers. | | 2026.8.2.post1 | v2026.8.2.post1 | 2026-08-02 | A packaging fix for the 2026.8.2 release. The wheel guard allowed markdown only at a bundled skill's `SKILL.md` plus two force-included pptx references, so `senior-unilp-manager`'s `assets/v4-reference.md` read as a forbidden entry and the tagged Windows release job failed for v2026.8.2 after the tag was already pushed — while a wheel that did build shipped `SKILL.md` links pointing at a file stripped from disk. `agentos/skills/bundled//assets/**` is now allowed (`references/` and stray top-level markdown stay forbidden) and a real-tree test over the bundled skills fails PR CI instead of the tagged release job. Cron prompt safety no longer rejects Unicode combining marks — Vietnamese and other scripts that need them pass again, while genuinely invisible marks stay blocked. | diff --git a/install.ps1 b/install.ps1 index da7e4ffc..c5b117a6 100644 --- a/install.ps1 +++ b/install.ps1 @@ -12,7 +12,7 @@ param( Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' -$defaultVersion = 'v2026.8.5' +$defaultVersion = 'v2026.8.6' $repoSlug = if ($env:AGENTOS_REPOSITORY) { $env:AGENTOS_REPOSITORY } else { 'use-agent-os/agent-os' } $pythonVersion = if ($env:AGENTOS_PYTHON_VERSION) { $env:AGENTOS_PYTHON_VERSION } else { '3.12' } $originalPath = if ($env:Path) { $env:Path } else { '' } diff --git a/install.sh b/install.sh index 10f935da..59c5109b 100755 --- a/install.sh +++ b/install.sh @@ -7,7 +7,7 @@ set -euo pipefail -default_version="v2026.8.5" +default_version="v2026.8.6" repo_slug="${AGENTOS_REPOSITORY:-use-agent-os/agent-os}" python_version="${AGENTOS_PYTHON_VERSION:-3.12}" original_path="${PATH:-}" @@ -18,10 +18,10 @@ cli_extras="" usage() { cat < None: diff --git a/tests/test_release_consistency.py b/tests/test_release_consistency.py index 59d4587c..199cec6e 100644 --- a/tests/test_release_consistency.py +++ b/tests/test_release_consistency.py @@ -3,7 +3,7 @@ import tomllib from pathlib import Path -CURRENT_VERSION = "2026.8.5" +CURRENT_VERSION = "2026.8.6" CURRENT_TAG = f"v{CURRENT_VERSION}" PREVIEW_VERSION = "0.0.1rc1" PREVIEW_TAG = f"v{PREVIEW_VERSION}" diff --git a/uv.lock b/uv.lock index c060e52c..a467914b 100644 --- a/uv.lock +++ b/uv.lock @@ -3721,7 +3721,7 @@ wheels = [ [[package]] name = "use-agent-os" -version = "2026.8.5" +version = "2026.8.6" source = { editable = "." } dependencies = [ { name = "aiosqlite" },