From 92831854efc758c614fcfc173cf0451e3eb68144 Mon Sep 17 00:00:00 2001 From: huangruiteng Date: Thu, 3 Sep 2026 02:13:28 +0800 Subject: [PATCH] chore(release): prepare v0.5.4 Signed-off-by: huangruiteng --- docs/book/chapters/00-reading-guide.md | 12 +++++------ docs/book/en/chapters/00-reading-guide.md | 14 ++++++------- docs/book/en/index.md | 6 +++--- docs/book/index.md | 6 +++--- docs/development/contributor-tasks.md | 4 ++-- docs/product/release-readiness.md | 6 ++++++ .../model-behavior-qualification-v0.md | 9 +++++--- loopx/__init__.py | 2 +- .../testing/doubao_model_behavior_actor.py | 21 +++++++++++-------- .../testing/model_tool_behavior.py | 18 +++++++++------- .../scoped_gate_successor_tool_behavior.py | 8 +++++-- .../testing/selected_todo_tool_behavior.py | 2 ++ .../terminal_settlement_tool_behavior.py | 19 ++++++++++------- man/loopx.1 | 2 +- pyproject.toml | 2 +- .../test_doubao_model_behavior_actor.py | 19 ++++++++++++++++- ...est_scoped_gate_successor_tool_behavior.py | 3 +++ .../test_selected_todo_tool_behavior.py | 7 +++++-- .../test_terminal_settlement_tool_behavior.py | 5 +++++ tests/test_license_metadata.py | 4 ++-- 20 files changed, 112 insertions(+), 57 deletions(-) diff --git a/docs/book/chapters/00-reading-guide.md b/docs/book/chapters/00-reading-guide.md index fd87e28165..5d880c87be 100644 --- a/docs/book/chapters/00-reading-guide.md +++ b/docs/book/chapters/00-reading-guide.md @@ -103,7 +103,7 @@ lifecycle 选择,不是所有贡献的默认终点。 ## 版本基线 -当前内容以 LoopX GitHub release `v0.5.3` 为发布锚点;本地命令示例已按 `loopx 0.5.3` 的 +当前内容以 LoopX GitHub release `v0.5.4` 为发布锚点;本地命令示例已按 `loopx 0.5.4` 的 公开 CLI 与协议表面复核。该版本要求 Python 3.11+ 与 Node.js 22.6+;后者运行由 LoopX 自动管理、 空闲后退出的 TypeScript Effect runtime,用户不需要手工维护 daemon。 @@ -129,7 +129,7 @@ node --version ### 如何理解当前 TypeScript 改造 -`v0.5.3` 不是“已经把 LoopX 全部重写成 TypeScript”。当前发布基线是: +`v0.5.4` 不是“已经把 LoopX 全部重写成 TypeScript”。当前发布基线是: - TypeScript 已拥有 Effect Program、Turn settlement、Todo completion、quota delivery routing、 workspace causality、scheduler transition/state 等迁移切片的 canonical semantics; @@ -140,18 +140,18 @@ node --version - 当前 `main` 已进入 transaction-payoff phase:后续以完整 transaction cutover 和删除旧语义 为进展单位,不继续堆 leaf helper 与 bridge。 -发布态以 `v0.5.3` tag 和 release notes 为准;迁移阶段、下一批 cutover 与最终 CLI/App 收敛以 +发布态以 `v0.5.4` tag 和 release notes 为准;迁移阶段、下一批 cutover 与最终 CLI/App 收敛以 [TypeScript Control-Plane Migration RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) 的当前状态为准。RFC 中的 Stage 3/4 仍是后续方向,不应写成已经发布。 -### 从 `v0.4.4` 阅读基线升级到 `v0.5.3` +### 从 `v0.4.4` 阅读基线升级到 `v0.5.4` 如果你读过旧版 Dev Book,优先校准四个变化: -| 主题 | `v0.5.3` 已发布事实 | 深入入口 | +| 主题 | `v0.5.4` 已发布事实 | 深入入口 | | --- | --- | --- | | Control Plane | 多个高风险 decision/effect 已迁入 typed TypeScript transaction owner;Python facade 仍承担迁移期边界 | [迁移 RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) | -| Operator surface | Personal Workspace 已提供 Goal、Task、Chat 和 read-only status source 入口;界面不是新的事实源 | [Dashboard README](https://github.com/huangruiteng/loopx/blob/v0.5.3/apps/presentation/dashboard/README.md) | +| Operator surface | Personal Workspace 已提供 Goal、Task、Chat 和 read-only status source 入口;界面不是新的事实源 | [Dashboard README](https://github.com/huangruiteng/loopx/blob/v0.5.4/apps/presentation/dashboard/README.md) | | Host runtime | Codex、Claude Code、OpenCode、Pi、KunlunCode、DeepSeek Harness 与 custom runner 具有不同 activation/stop contract | [Runtime Connector Catalog](/loopx/docs/integrations/runtime-connector-catalog/) | | Capability / Provider | Capability 由 package-owned catalog entry、真实 command 和 durable validation 共同定义;Provider/Extension 不继承 Kernel authority | [Capability Catalog](/loopx/docs/capabilities/) | diff --git a/docs/book/en/chapters/00-reading-guide.md b/docs/book/en/chapters/00-reading-guide.md index 50517feb1f..f46d133943 100644 --- a/docs/book/en/chapters/00-reading-guide.md +++ b/docs/book/en/chapters/00-reading-guide.md @@ -114,8 +114,8 @@ Do not bypass a newer permission or lifecycle check just to make an older exampl ## Version baseline -The current release anchor is LoopX GitHub release `v0.5.3`. Local command examples were checked against -the public `loopx 0.5.3` CLI and protocol surface. This release requires Python 3.11+ and Node.js 22.6+. +The current release anchor is LoopX GitHub release `v0.5.4`. Local command examples were checked against +the public `loopx 0.5.4` CLI and protocol surface. This release requires Python 3.11+ and Node.js 22.6+. LoopX starts and reuses its managed, idle-exiting TypeScript Effect runtime automatically; users do not operate that runtime as a manual daemon. @@ -143,7 +143,7 @@ different version identifiers imply. ### How to read the TypeScript migration -`v0.5.3` does not mean that all of LoopX has been rewritten in TypeScript. The current release baseline is: +`v0.5.4` does not mean that all of LoopX has been rewritten in TypeScript. The current release baseline is: - TypeScript owns the canonical semantics for migrated slices of the Effect Program, Turn settlement, Todo completion, quota delivery routing, workspace causality, and scheduler transition/state; @@ -154,19 +154,19 @@ different version identifiers imply. - current `main` is in the transaction-payoff phase: later progress is measured by complete transaction cutovers and deleted legacy semantics, not by accumulating leaf helpers and bridge calls. -Treat the `v0.5.3` tag and release notes as the shipped baseline. Use the current status of the +Treat the `v0.5.4` tag and release notes as the shipped baseline. Use the current status of the [TypeScript Control-Plane Migration RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) for later cutovers and final CLI/App convergence. RFC Stages 3 and 4 remain future direction, not shipped behavior. -### Updating your mental model from `v0.4.4` to `v0.5.3` +### Updating your mental model from `v0.4.4` to `v0.5.4` If you read an earlier edition of the Dev Book, recalibrate these four areas first: -| Area | Shipped in `v0.5.3` | Continue with | +| Area | Shipped in `v0.5.4` | Continue with | | --- | --- | --- | | Control Plane | Several high-risk decisions and effects have typed TypeScript transaction owners; Python facades still carry migration-time boundaries | [Migration RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) | -| Operator surface | Personal Workspace exposes Goal, Task, Chat, and read-only status-source entrypoints; the UI is not a new source of truth | [Dashboard README](https://github.com/huangruiteng/loopx/blob/v0.5.3/apps/presentation/dashboard/README.md) | +| Operator surface | Personal Workspace exposes Goal, Task, Chat, and read-only status-source entrypoints; the UI is not a new source of truth | [Dashboard README](https://github.com/huangruiteng/loopx/blob/v0.5.4/apps/presentation/dashboard/README.md) | | Host runtime | Codex, Claude Code, OpenCode, Pi, KunlunCode, DeepSeek Harness, and custom runners have distinct activation and stop contracts | [Runtime Connector Catalog](/loopx/docs/integrations/runtime-connector-catalog/) | | Capability and Provider | A Capability is defined by a package-owned catalog entry, a real command, and durable validation; a Provider or Extension does not inherit Kernel authority | [Capability Catalog](/loopx/docs/capabilities/) | diff --git a/docs/book/en/index.md b/docs/book/en/index.md index 99b2f35ec8..2326c8e3e5 100644 --- a/docs/book/en/index.md +++ b/docs/book/en/index.md @@ -79,16 +79,16 @@ versioned or optional functionality, not the default shape of every contribution - Source format: Markdown - Site generator: MkDocs Material - Hosting: GitHub Pages -- LoopX release anchor: `v0.5.3` +- LoopX release anchor: `v0.5.4` - Runtime prerequisites: Python 3.11+ and Node.js 22.6+ The official public protocols remain authoritative for protocol facts. Commands that change across releases remain authoritative in the release you use, its current `--help`, and official documentation. This book owns the learning path and explanatory model, not another complete command reference. -In `v0.5.3`, key semantics for the Effect Program, Turn settlement, Todo completion, quota delivery +In `v0.5.4`, key semantics for the Effect Program, Turn settlement, Todo completion, quota delivery routing, workspace causality, and scheduler state sit behind typed TypeScript owners. During the migration, the Python CLI still owns transport, legacy projections, external Provider calls, and some durable writeback. These are not two independently evolving control planes. Treat the [TypeScript Control-Plane Migration RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) -on current `main` as the direction for later stages, not as shipped `v0.5.3` behavior. +on current `main` as the direction for later stages, not as shipped `v0.5.4` behavior. diff --git a/docs/book/index.md b/docs/book/index.md index f41c0607ff..f5de75a024 100644 --- a/docs/book/index.md +++ b/docs/book/index.md @@ -70,15 +70,15 @@ Extension 只是可独立版本化和交付的一种路径。 - 正文格式:Markdown; - 站点生成器:MkDocs Material; - 在线发布:GitHub Pages; -- LoopX 发布锚点:`v0.5.3`; +- LoopX 发布锚点:`v0.5.4`; - 运行时前提:Python 3.11+ 与 Node.js 22.6+。 协议解释以 LoopX 官方公开合同为事实源。易变化的命令仍以对应发布物、官方文档和当前 `--help` 为准;本书负责教学顺序与心智模型,不成为另一份完整命令参考。 -`v0.5.3` 已把 Effect Program、Turn settlement、Todo completion、quota delivery routing、 +`v0.5.4` 已把 Effect Program、Turn settlement、Todo completion、quota delivery routing、 workspace causality 与 scheduler state 等关键语义放到 typed TypeScript owner 后面。Python CLI 仍负责迁移期 transport、兼容投影、外部 Provider 调用和部分持久化写回;这不是两套可独立演进的 控制面。当前 `main` 的后续迁移阶段以 [TypeScript Control-Plane Migration RFC](/loopx/docs/architecture/rfcs/typescript-control-plane-migration-v0/) -为准,不应倒推为 `v0.5.3` 已发布行为。 +为准,不应倒推为 `v0.5.4` 已发布行为。 diff --git a/docs/development/contributor-tasks.md b/docs/development/contributor-tasks.md index ef490e991c..c83c33a2b0 100644 --- a/docs/development/contributor-tasks.md +++ b/docs/development/contributor-tasks.md @@ -103,7 +103,7 @@ points: | Runtime connectors and content workflows | DeepSeek Harness Turn adapter with real e2e smoke landed (#3188); OpenCode 1 and OpenCode 2 continuous goal loops ship (#3151); content-ops gained a layout template library and dense-cover defaults (#3222, #3223); provider-neutral PR program snapshots ship (#2814); `computer_use_runtime_v0` is now a machine-checkable protocol contract (#3279); a `loopx-community-discussion` public source provider (#3299) and a `loopx-repo-health` provider (#3272) extend public source coverage; goal-channel botmux runtime integration lands with terminal dispatches preserved and uncertain dispatches persisted, desktop chat routes through a loopback service, chat-wide routing and contextual inbox replies land (#3555), and managed and queued turn creation are serialized (#3542, #3562); the desktop added workspace language settings, localized write previews, and preserved heartbeat schedule and off-hours semantics (#3594); host parity, skill-delivery, and observable-handle thin pytest now cover the expanded host list. Post-v0.5.3, bot document comment scopes read from the published app (#3722), turn-start reaction replay is bounded (#3733), and the DSH plugin is one-step ready (#3725). | Add one provider-neutral parity slice for DeepSeek Harness, OpenCode 1/2, goal-channel botmux, content-ops delivery/readback, or desktop locale parity; keep raw transcripts, provider payloads, credentials, and host-local paths out of fixtures. | | Benchmark boundary | Benchmark research was reset around native runners (#3267): native Codex Goal connects to the real runtime (#3271), workers are isolated from the host (#3277), provider env binds to installed Goal profiles (#3276) with formally verified treatments (#3275) and live continuations (#3273), runtime evidence binds to the exact container (#3303), a post-run case analyst brief ships (#3289), an integrity qualification toolkit landed (#3241), source-env usage is distinguished from credential probes (#3298, #3278), and accountable closeout requires Todo validation (#3229). A provider-neutral four-arm study contract ships (#3516), alongside restricted task-source access (#3504), non-http git clones classified as network with loopback integrity probes allowed (#3510), namespaced public case ids (#3532), locked git-clone integrity boundaries (#3515), orchestrator runtime provenance, and fail-closed runtime closeout drift (#3503). Deterministic fixtures already cover GH-C99 without live scoring or uploads: `tests/capabilities/test_benchmark_four_arm_contract.py`, `tests/capabilities/test_benchmark_experiment_board.py` (orchestrator runtime provenance negatives), `tests/capabilities/test_benchmark_runtime_continuity.py` (fail-closed closeout drift), and integrity/network/task-source cases in `tests/capabilities/test_benchmark_toolkit.py`; the covered GH-C99 fixture task was retired (#3653). Public native Goal trajectory summaries now derive from compact lifecycle facts without retaining raw artifacts (#3327), completing the GH-C16 slice. Post-v0.5.3, an external agent phase ships (#3565), restricted-access suspicion adjudicates by causal use (#3747), the plan-role fidelity gate is removed (#3753), and retired fidelity references are cleaned up (#3756). Shared lifecycle, readiness, ledger, and reducer contracts remain the public seam; generic Effect Program conformance and replay tests harden settlement infrastructure but do not change benchmark scoring or authorize live runs. Live scored comparisons stay held until a fresh task-free runner lifecycle receipt proves readiness. | Extend synthetic setup/termination attribution, derive the public trajectory summary for a second non-SkillsBench adapter, or add a SWE adapter only when a second SWE route needs shared launch/observe/ingest behavior. Do not launch scoring, duplicate the controller, or expose raw task text, logs, trajectories, verifier tails, credentials, uploads, or local paths. | | Validation and change quality | Python tests are green on the latest runtime-bearing `main` change; public smoke parity and the frontstage Pages build are restored, with Codex App fallback receipt identity isolated (#3233); public smoke reliability was restored again (#3302), stargazer history now fetches through REST (#3320), and a repository-hygiene smoke plus release-timeline ratchet landed (#3249). The Auto Research KNN evidence-normalization smoke now gates improved/contradicted status and protected-scope checks on semantics-derived public eval fixtures rather than wall-clock speedup, completing GH-C77. Post-v0.5.3, the remaining public smoke boundaries are repaired (#3740) and public smoke fixtures are fixed (#3739). | Retain negative/mutation coverage on deterministic fixtures, and distinguish infrastructure outages from product regressions. Keep live model/provider checks explicit and low-frequency. | -| Release and install | v0.5.3 is the latest public tag and package version (v0.5.0-v0.5.3 landed after the last board refresh). PyPI remains the default complete install path (#3301) with explicit installation ownership (#3566); canonical project links are published (#3253), derived capability/manpage surfaces are synced (#3254), exact Miaoda releases are verified in periodic-report receipts (#3508) with governed delivery (#3401), extension doctor readiness recovers across releases (#3556), doctor deep checks validate by install kind (#3736), and the public release timeline covers v0.1.3 through v0.5.3. | Keep install, activation, and recovery guidance aligned with the PyPI default and tagged stable versus post-tag `main`, and continue contributor-safe update recovery without adding a parallel release checklist. | +| Release and install | v0.5.4 is the latest public tag and package version (v0.5.0-v0.5.4 landed after the last board refresh). PyPI remains the default complete install path (#3301) with explicit installation ownership (#3566); canonical project links are published (#3253), derived capability/manpage surfaces are synced (#3254), exact Miaoda releases are verified in periodic-report receipts (#3508) with governed delivery (#3401), extension doctor readiness recovers across releases (#3556), doctor deep checks validate by install kind (#3736), and the public release timeline covers v0.1.3 through v0.5.4. | Keep install, activation, and recovery guidance aligned with the PyPI default and tagged stable versus post-tag `main`, and continue contributor-safe update recovery without adding a parallel release checklist. | | Public docs and onboarding | Hosted docs, a public homepage, the Dev Book, localized dashboard copy, public/private boundary examples, GitHub issue forms, and a PR/issue label taxonomy have landed. Slash-command installation now exposes all four canonical global manager commands through their shipped CLI wrappers; fresh-project onboarding and its regression fixture landed (#3093, #3103), and harness-above positioning (#3202), ecosystem adoption and derivatives inventory (#3224), GitHub maintenance and ops automation best practices (#3227), long-horizon/commercialization strategy docs (#3217-#3220), the TypeScript control-plane migration RFC (#3226), the open strategy review process (#3295), stale-issue reminders without auto-close (#3297), welcome-all-contribution-shapes guidance (#3294), consolidated community policy (#3238), Apache-2.0 open-core adoption (#3235), a capability implementation code map (#3252) with co-located docs (#3265), outcome/extension path clarifications (#3242), the NoKV semantic-authority RFC (#3263), the long-horizon benchmark research program RFC (#3240), the published technical directions (#3248), and a DCO sign-off reminder (#3316) are public. The Auto Research stop/takeover/state-aware-wake walkthrough (GH-C43) documents the shipped control cycle without a second launcher or README first-screen change. Post-v0.5.3, the planner-worker operator guide (#3630), the hosted-docs navigation and locale-parity check (#3632), and the atomic-promotion failure matrix (#3633) land; the GH-C49 frontstage legibility polish (#3663), host no-spend parity (#3661), reward-memory walkthrough (#3659), governance Stage-2 refresh (#3657), and KNN closeout (#3654) are merged. | Keep contributor, release, protocol, course, showcase, and RFC surfaces concise and linked to public evidence; add navigation, locale, and RFC-compatibility checks without appending status narratives or aliases. | ## Turn Loop Controller Plan @@ -138,7 +138,7 @@ points for contributors who are still learning the repository. | ID | Area | Task | Validation | | --- | --- | --- | --- | | GH-C02 | tests | Claimed: a PR is open (#3623) extending the todo-lifecycle smoke with archive-completed coverage. Review it at exact head or add the omit/archive negative the review finds missing. | `python3 examples/control_plane/todo-lifecycle-cli-smoke.py` and `python3 -m py_compile loopx/*.py` | -| GH-C04 | docs | Keep release docs current through v0.5.4: align install, activation, and recovery guidance with the PyPI default complete install path (#3301) and explicit installation ownership (#3566), preserve tagged stable vs post-tag `main` and release-snapshot vs canary distinctions, cover installed-runtime activation recovery and extension-doctor readiness (#3556), and keep the public release timeline (v0.1.3-v0.5.3) in sync with tagged evidence instead of duplicating the release body's bilingual optional-capability usage guidance. | `python3 examples/fresh-clone-quickstart-smoke.py`, `python3 examples/loopx-update-smoke.py`, `python3 examples/release/release-readiness-doc-smoke.py`, `python3 examples/release/release-version-contract-smoke.py`, and `loopx check --scan-path docs/product/release-readiness.md --scan-path CONTRIBUTING.md` | +| GH-C04 | docs | Keep release docs current through v0.5.4: align install, activation, and recovery guidance with the PyPI default complete install path (#3301) and explicit installation ownership (#3566), preserve tagged stable vs post-tag `main` and release-snapshot vs canary distinctions, cover installed-runtime activation recovery and extension-doctor readiness (#3556), and keep the public release timeline (v0.1.3-v0.5.4) in sync with tagged evidence instead of duplicating the release body's bilingual optional-capability usage guidance. | `python3 examples/fresh-clone-quickstart-smoke.py`, `python3 examples/loopx-update-smoke.py`, `python3 examples/release/release-readiness-doc-smoke.py`, `python3 examples/release/release-version-contract-smoke.py`, and `loopx check --scan-path docs/product/release-readiness.md --scan-path CONTRIBUTING.md` | ### Focused Implementation Small-to-medium code changes with a clear validation surface. These are good diff --git a/docs/product/release-readiness.md b/docs/product/release-readiness.md index ecb54d4118..5e7e11794c 100644 --- a/docs/product/release-readiness.md +++ b/docs/product/release-readiness.md @@ -525,6 +525,12 @@ path, and canary route rather than as a user-facing release baseline. Pi task-lease facade; it also strengthens Lark inbox routing and catch-up, typed Todo/quota/scheduler settlement, repository delivery admission, and runtime startup recovery. +- `v0.5.4` on 2026-09-03: typed control-plane and governed-workflow release at + the matching `v0.5.4` tag. LoopX moves more Todo, task-lease, quota, + scheduler, Vision, and replan transactions behind TypeScript owners; + advances staged file, PostgreSQL, and NoKV shared-authority providers; + completes the periodic-report lifecycle; makes the DSH plugin one-step ready; and adds + public-safe benchmark study projection without granting upload authority. When a new public release is promoted, add it here only after the matching tag, release note, stable ref, update path, and focused release canary agree. diff --git a/docs/reference/protocols/model-behavior-qualification-v0.md b/docs/reference/protocols/model-behavior-qualification-v0.md index 29f882e6ee..2cba45d0c8 100644 --- a/docs/reference/protocols/model-behavior-qualification-v0.md +++ b/docs/reference/protocols/model-behavior-qualification-v0.md @@ -142,7 +142,8 @@ state and are never a public repository artifact. ## Direct Doubao Shadow Actor `DoubaoModelBehaviorActor` calls only the canonical Ark Chat Completions -endpoint and allowlists the versioned Doubao 2.1 Pro and Turbo model ids. It +endpoint and explicitly allowlists the versioned Doubao 2.1 Pro and Turbo +model ids plus the rolling `doubao-seed-evolving` model id. It does not accept an arbitrary base URL, does not follow redirects, does not send tool definitions, and converts transport failures into bounded errors without provider response bodies. @@ -166,8 +167,10 @@ closed when the candidate actually omits or changes selected work. Live use requires `ARK_API_KEY` to be injected into the process environment. The key is held only by the in-memory adapter and is never placed in a LoopX packet, receipt, error, command argument, fixture, or repository file. The -optional `LOOPX_MODEL_BEHAVIOR_MODEL` selector can choose one of the two -allowlisted Doubao 2.1 model ids. Missing credentials, unsupported models, +optional `LOOPX_MODEL_BEHAVIOR_MODEL` selector can choose one of those +explicitly allowlisted model ids. A `doubao-seed-evolving` receipt qualifies +only the model alias observed at that release commit and run time; it does not +claim an immutable provider revision. Missing credentials, unsupported models, malformed provider JSON, or non-conforming decisions fail closed. LoopX does not search credential stores and does not route these calls through a memory system or another agent service. diff --git a/loopx/__init__.py b/loopx/__init__.py index b728befafa..650d48b698 100644 --- a/loopx/__init__.py +++ b/loopx/__init__.py @@ -2,4 +2,4 @@ __all__ = ["__version__"] -__version__ = "0.5.3" +__version__ = "0.5.4" diff --git a/loopx/control_plane/testing/doubao_model_behavior_actor.py b/loopx/control_plane/testing/doubao_model_behavior_actor.py index 38b3faaace..0b846badea 100644 --- a/loopx/control_plane/testing/doubao_model_behavior_actor.py +++ b/loopx/control_plane/testing/doubao_model_behavior_actor.py @@ -26,6 +26,7 @@ DOUBAO_2_1_PRO_MODEL = "doubao-seed-2-1-pro-260628" DOUBAO_2_1_TURBO_MODEL = "doubao-seed-2-1-turbo-260628" +DOUBAO_SEED_EVOLVING_MODEL = "doubao-seed-evolving" DOUBAO_CHAT_COMPLETIONS_ENDPOINT = ( "https://ark.cn-beijing.volces.com/api/v3/chat/completions" ) @@ -33,7 +34,13 @@ DOUBAO_MODEL_ENV = "LOOPX_MODEL_BEHAVIOR_MODEL" MODEL_BEHAVIOR_PROVIDER_INPUT_SCHEMA_VERSION = "model_behavior_provider_input_v0" -_ALLOWED_MODELS = {DOUBAO_2_1_PRO_MODEL, DOUBAO_2_1_TURBO_MODEL} +ALLOWED_MODEL_BEHAVIOR_MODELS = frozenset( + { + DOUBAO_2_1_PRO_MODEL, + DOUBAO_2_1_TURBO_MODEL, + DOUBAO_SEED_EVOLVING_MODEL, + } +) _MAX_PROVIDER_RESPONSE_BYTES = 1_048_576 _MAX_DECISION_TOKENS = 4096 @@ -523,10 +530,8 @@ def __init__( ) -> None: if not api_key.strip(): raise RuntimeError("Doubao actor requires a runtime-injected API key") - if model not in _ALLOWED_MODELS: - raise ValueError( - "Doubao actor model must be an allowlisted Doubao 2.1 model" - ) + if model not in ALLOWED_MODEL_BEHAVIOR_MODELS: + raise ValueError("Doubao actor model must be explicitly allowlisted") if timeout_seconds <= 0 or timeout_seconds > 300: raise ValueError("Doubao actor timeout must be between 0 and 300 seconds") self._api_key = api_key @@ -598,10 +603,8 @@ def __init__( ) -> None: if not api_key.strip(): raise RuntimeError("Doubao actor requires a runtime-injected API key") - if model not in _ALLOWED_MODELS: - raise ValueError( - "Doubao actor model must be an allowlisted Doubao 2.1 model" - ) + if model not in ALLOWED_MODEL_BEHAVIOR_MODELS: + raise ValueError("Doubao actor model must be explicitly allowlisted") if timeout_seconds <= 0 or timeout_seconds > 300: raise ValueError("Doubao actor timeout must be between 0 and 300 seconds") self._api_key = api_key diff --git a/loopx/control_plane/testing/model_tool_behavior.py b/loopx/control_plane/testing/model_tool_behavior.py index befc29d59e..ee5a700479 100644 --- a/loopx/control_plane/testing/model_tool_behavior.py +++ b/loopx/control_plane/testing/model_tool_behavior.py @@ -12,8 +12,7 @@ from typing import Any from .doubao_model_behavior_actor import ( - DOUBAO_2_1_PRO_MODEL, - DOUBAO_2_1_TURBO_MODEL, + ALLOWED_MODEL_BEHAVIOR_MODELS, DOUBAO_CHAT_COMPLETIONS_ENDPOINT, DoubaoActorTransport, DoubaoActorTransportError, @@ -42,7 +41,14 @@ MAX_TOOL_ARGUMENT_BYTES = 8_192 MAX_PROVIDER_TOKENS = 2_048 -ALLOWED_MODELS = {DOUBAO_2_1_PRO_MODEL, DOUBAO_2_1_TURBO_MODEL} + +QUOTA_FIRST_TOOL_INSTRUCTION = ( + "Before any workspace read, diagnostic, discovery, clock lookup, or state " + "inspection, the first tool call must execute the exact quota guard command " + "shown in the heartbeat task. It may set only LOOPX_TURN inline before that " + "guard; do not prepend export, echo, or any other shell command. Treat the " + "returned interaction_contract as the authority for every later action. " +) @dataclass(frozen=True) @@ -203,10 +209,8 @@ def __init__( ) -> None: if not api_key.strip(): raise RuntimeError("Doubao actor requires a runtime-injected API key") - if model not in ALLOWED_MODELS: - raise ValueError( - "Doubao actor model must be an allowlisted Doubao 2.1 model" - ) + if model not in ALLOWED_MODEL_BEHAVIOR_MODELS: + raise ValueError("Doubao actor model must be explicitly allowlisted") if timeout_seconds <= 0 or timeout_seconds > 300: raise ValueError("Doubao actor timeout must be between 0 and 300 seconds") self._api_key = api_key diff --git a/loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py b/loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py index 87aa848259..b68b667c27 100644 --- a/loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +++ b/loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py @@ -20,6 +20,7 @@ ) from .model_tool_behavior import ( DoubaoExecToolClient, + QUOTA_FIRST_TOOL_INSTRUCTION, digest_text, execute_loopx_cli, ) @@ -353,14 +354,17 @@ def qualify( "content": ( "You are Codex running one LoopX heartbeat. Follow the " "heartbeat task and use the available shell tool when needed. " + f"{QUOTA_FIRST_TOOL_INSTRUCTION}" "Choose each next action from the latest tool result. If quota " "requires a user action but still selects runnable work, surface " "the user notice as non-blocking in the final assistant response " "after executing the selected Todo tool action; do not wait on " "that notice. The quota user-action text is sufficient for the " "notice and grants no authority to inspect or act on the user-gate " - "target. After the selected Todo tool succeeds, call no more tools " - "and return the final response." + "target. When selected_todo.text names one relative file, the " + "next tool call must be `cat `; do not run pwd, " + "ls, find, rg, or inspect state first. After the selected Todo " + "tool succeeds, call no more tools and return the final response." ), }, {"role": "user", "content": fixture.task_body}, diff --git a/loopx/control_plane/testing/selected_todo_tool_behavior.py b/loopx/control_plane/testing/selected_todo_tool_behavior.py index 4e01c66a28..dd62909dc1 100644 --- a/loopx/control_plane/testing/selected_todo_tool_behavior.py +++ b/loopx/control_plane/testing/selected_todo_tool_behavior.py @@ -21,6 +21,7 @@ ) from .model_tool_behavior import ( DoubaoExecToolClient, + QUOTA_FIRST_TOOL_INSTRUCTION, argument_value, digest_text, execute_loopx_cli, @@ -1017,6 +1018,7 @@ def qualify( "content": ( "You are Codex running one LoopX heartbeat. Follow the " "heartbeat task and use the available shell tool when needed. " + f"{QUOTA_FIRST_TOOL_INSTRUCTION}" "The shell working directory is the connected goal project " "root; resolve relative paths from the selected Todo there. " "Choose each next action from the latest tool result. If quota " diff --git a/loopx/control_plane/testing/terminal_settlement_tool_behavior.py b/loopx/control_plane/testing/terminal_settlement_tool_behavior.py index 161df98265..2895f9c3fb 100644 --- a/loopx/control_plane/testing/terminal_settlement_tool_behavior.py +++ b/loopx/control_plane/testing/terminal_settlement_tool_behavior.py @@ -22,6 +22,7 @@ ) from .model_tool_behavior import ( DoubaoExecToolClient, + QUOTA_FIRST_TOOL_INSTRUCTION, argument_value, digest_text, execute_loopx_cli, @@ -829,14 +830,18 @@ def qualify( "content": ( "You are Codex running one LoopX heartbeat. Follow the " "production heartbeat task and choose each next action " - "from the latest tool result. The exec tool's current " - "workspace is the connected goal project root, so task " + "from the latest tool result. " + f"{QUOTA_FIRST_TOOL_INSTRUCTION}" + "The exec tool's current workspace is the connected goal " + "project root, so task " "paths are directly addressable. Read the exact proof target " - "once with `cat`; do not rediscover, list, or re-read the " - "workspace after it validates. Use one command per tool call. " - "After validation, fill the quota-projected writeback " - "placeholders from the proof's typed fields and follow the " - "ordered settlement plan." + "once with `cat`; the host validates that tool result. Do not " + "run python, jq, test, echo, another validation command, or " + "re-read the workspace afterward. Use one command per tool " + "call. The tool call immediately after the proof read must " + "fill and execute quota-projected next_cli_actions[0] from " + "the proof's typed fields, then follow the ordered settlement " + "plan." ), }, {"role": "user", "content": fixture.task_body}, diff --git a/man/loopx.1 b/man/loopx.1 index 91515131bd..576cc70fdd 100644 --- a/man/loopx.1 +++ b/man/loopx.1 @@ -1,4 +1,4 @@ -.TH LOOPX 1 "" "LoopX 0.5.3" "User Commands" +.TH LOOPX 1 "" "LoopX 0.5.4" "User Commands" .SH NAME loopx \- control-plane helper for long-running agent work .SH SYNOPSIS diff --git a/pyproject.toml b/pyproject.toml index 4ed7e39de6..2e0609f367 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "loopx" -version = "0.5.3" +version = "0.5.4" description = "A lightweight Loop Engineering control plane for long-running agent goals." readme = "README.md" requires-python = ">=3.11" diff --git a/tests/control_plane/test_doubao_model_behavior_actor.py b/tests/control_plane/test_doubao_model_behavior_actor.py index 4254a69fb3..bbae2fd14d 100644 --- a/tests/control_plane/test_doubao_model_behavior_actor.py +++ b/tests/control_plane/test_doubao_model_behavior_actor.py @@ -11,6 +11,7 @@ from loopx.control_plane.testing.doubao_model_behavior_actor import ( DOUBAO_2_1_PRO_MODEL, DOUBAO_CHAT_COMPLETIONS_ENDPOINT, + DOUBAO_SEED_EVOLVING_MODEL, MODEL_BEHAVIOR_PROVIDER_INPUT_SCHEMA_VERSION, DoubaoActorTransportError, DoubaoModelBehaviorActor, @@ -247,7 +248,7 @@ def test_environment_factory_fails_closed_without_injected_key() -> None: with pytest.raises(RuntimeError, match="ARK_API_KEY is not injected"): DoubaoModelBehaviorActor.from_environment(environ={}) - with pytest.raises(ValueError, match="allowlisted Doubao 2.1"): + with pytest.raises(ValueError, match="explicitly allowlisted"): DoubaoModelBehaviorActor.from_environment( environ={ "ARK_API_KEY": "fixture-key-not-a-secret", @@ -256,6 +257,22 @@ def test_environment_factory_fails_closed_without_injected_key() -> None: ) +def test_environment_factory_accepts_doubao_seed_evolving() -> None: + actor = DoubaoModelBehaviorActor.from_environment( + environ={ + "ARK_API_KEY": "fixture-key-not-a-secret", + "LOOPX_MODEL_BEHAVIOR_MODEL": DOUBAO_SEED_EVOLVING_MODEL, + }, + transport=lambda **_: { + "choices": [{"message": {"content": json.dumps(_decision())}}] + }, + ) + + result = normalize_model_behavior_actor_result(actor(_request())) + + assert result["actor_ref"] == f"ark:{DOUBAO_SEED_EVOLVING_MODEL}" + + @pytest.mark.parametrize( "response, message", [ diff --git a/tests/control_plane/test_scoped_gate_successor_tool_behavior.py b/tests/control_plane/test_scoped_gate_successor_tool_behavior.py index b0d9b74076..41bf9118c9 100644 --- a/tests/control_plane/test_scoped_gate_successor_tool_behavior.py +++ b/tests/control_plane/test_scoped_gate_successor_tool_behavior.py @@ -6,6 +6,7 @@ from typing import Any from loopx.control_plane.testing.model_tool_behavior import ( + QUOTA_FIRST_TOOL_INSTRUCTION, scripted_assistant_response as _stop_response, scripted_exec_tool_response as _tool_response, ) @@ -99,9 +100,11 @@ def transport(**kwargs: Any) -> Mapping[str, Any]: ] is True assert "response_plan" not in quota_result["interaction_contract"] system_prompt = requests[0]["messages"][0]["content"] + assert QUOTA_FIRST_TOOL_INSTRUCTION in system_prompt assert "grants no authority to inspect or act on the user-gate target" in ( system_prompt ) + assert "next tool call must be `cat `" in system_prompt assert "After the selected Todo tool succeeds, call no more tools" in ( system_prompt ) diff --git a/tests/control_plane/test_selected_todo_tool_behavior.py b/tests/control_plane/test_selected_todo_tool_behavior.py index 55ae24a114..b040cdf6fd 100644 --- a/tests/control_plane/test_selected_todo_tool_behavior.py +++ b/tests/control_plane/test_selected_todo_tool_behavior.py @@ -6,6 +6,7 @@ from typing import Any from loopx.control_plane.testing.model_tool_behavior import ( + QUOTA_FIRST_TOOL_INSTRUCTION, scripted_exec_tool_response as _tool_response, ) from loopx.control_plane.testing.selected_todo_tool_behavior import ( @@ -73,8 +74,10 @@ def transport(**kwargs: Any) -> Mapping[str, Any]: "role": "system", "content": ( "You are Codex running one LoopX heartbeat. Follow the heartbeat " - "task and use the available shell tool when needed. The shell " - "working directory is the connected goal project root; resolve " + "task and use the available shell tool when needed. " + f"{QUOTA_FIRST_TOOL_INSTRUCTION}" + "The shell working directory is the connected goal project " + "root; resolve " "relative paths from the selected Todo there. Choose each next " "action from the latest tool result. If quota sets " "selection_required, choose any currently eligible Todo; " diff --git a/tests/control_plane/test_terminal_settlement_tool_behavior.py b/tests/control_plane/test_terminal_settlement_tool_behavior.py index 5af035aa9f..fa506b8265 100644 --- a/tests/control_plane/test_terminal_settlement_tool_behavior.py +++ b/tests/control_plane/test_terminal_settlement_tool_behavior.py @@ -6,6 +6,7 @@ from typing import Any from loopx.control_plane.testing.model_tool_behavior import ( + QUOTA_FIRST_TOOL_INSTRUCTION, ScriptedAssistantAction, ScriptedDoubaoExecTransport, ScriptedExecToolAction, @@ -166,6 +167,10 @@ def test_real_tool_loop_settles_final_todo_after_spend(tmp_path: Path) -> None: assert "settlement-proof.json" not in json.dumps(receipt, sort_keys=True) first = transport.requests[0] + system_prompt = first["messages"][0]["content"] + assert QUOTA_FIRST_TOOL_INSTRUCTION in system_prompt + assert "the host validates that tool result" in system_prompt + assert "immediately after the proof read" in system_prompt assert first["messages"][1] == {"role": "user", "content": fixture.task_body} assert TERMINAL_SETTLEMENT_FIXTURE_TODO_ID not in fixture.task_body quota_result = _latest_quota_packet(transport.requests[2]) diff --git a/tests/test_license_metadata.py b/tests/test_license_metadata.py index 98528dd854..0131b6a9c1 100644 --- a/tests/test_license_metadata.py +++ b/tests/test_license_metadata.py @@ -31,8 +31,8 @@ def test_root_license_is_canonical_apache_2_with_historical_notices() -> None: def test_python_distributions_declare_apache_2() -> None: root_project = _project_metadata("pyproject.toml") - assert root_project["version"] == "0.5.3" - assert '__version__ = "0.5.3"' in (ROOT / "loopx/__init__.py").read_text() + assert root_project["version"] == "0.5.4" + assert '__version__ = "0.5.4"' in (ROOT / "loopx/__init__.py").read_text() assert root_project["license"] == "Apache-2.0" assert set(root_project["license-files"]) == {"LICENSE", "NOTICE", "LICENSE-MIT"}