Action Runtime: honest-status across gateway exec paths (+ model-picker, SessionState) - #43039
Closed
claw3649 wants to merge 16 commits into
Closed
Action Runtime: honest-status across gateway exec paths (+ model-picker, SessionState)#43039claw3649 wants to merge 16 commits into
claw3649 wants to merge 16 commits into
Conversation
…res in the UI Model-resolution reliability and picker UX, including the corrections found in review of the in-flight batch: - model_switch: discover keyless local endpoints (ollama/llama.cpp/LM Studio) so `ollama rm`'d models stop lingering in the picker — but only when no explicit `models:` subset is pinned, preserving an intentional narrowing (parity with the custom-providers section; fixes 2 test regressions). - models: bypass the disk cache for custom/local providers (always live), falling through to the shared stale-beats-empty path so a transiently unreachable endpoint keeps its last-known-good list instead of going empty. - model_metadata: pin local Gemma 4 26B to its served 64k num_ctx ahead of the /api/show GGUF-max probe so the token budget can't over-run and silently truncate; reconcile a stale cache entry in place instead of re-probing every call (the previous guard never converged). - web/desktop picker: await the /model switch and surface a rejected switch (result.error) inline — keep the dialog open, roll back the optimistic model, toast benign advisories — instead of closing as though it worked. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- launchd_restart: try a graceful SIGUSR1 self-restart before terminate, with a hard-stop fallback; bind the force-kill to the freshly-resolved live PID (not the captured one) so PID reuse can't SIGKILL an unrelated process. - hermes launcher: re-exec into ./venv/bin/python on Python <3.11, but only after verifying that interpreter is itself >=3.11 — a stale venv no longer silently bypasses the version guard and crashes later with an opaque error. - tests: cover the SIGUSR1 graceful path and reconcile the terminate-drain fallback test with the new graceful-first behavior. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ontract Phase 2 of the Central Brain + Action Runtime design (docs/architecture): a single honest-status schema the gateway's exec handlers funnel through. - contract: ExecutionTask / ExecutionResult / Status / ErrorType / ExecError / SideEffect / NeedsInput / Constraints. `outputs` is a lossless payload bag so each handler's native shape round-trips back to its exact wire dict. - adapters: shell / cli / plugin / slash, each a pure (to_result, to_wire) pair with a byte-identical round-trip guarantee. - tests assert the round-trip identity (the byte-compat oracle). Pure dataclasses with no dependency on the gateway (server imports this, not the reverse). Nothing consumes the rich fields yet — that is Phase 4. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… Runtime - slash.exec: report a rejected live side-effect (/model, busy) and a raised plugin handler as a structured result.error (additive — TUI/web that read only output/warning are unaffected), keying off _SlashSideEffect.kind rather than prefix-matching the message text. - shell.exec / cli.exec / slash.exec / plugin path now build an ExecutionResult and render it back through the adapters — byte-identical wire, so the three frontends need no change. A non-zero exit is an honest FAILED result whose wire shape (result.code) is unchanged. - desktop typed `/model` path: surface result.error instead of rendering a rejected switch as a success line; add error? to SlashExecResponse. - add honest-status regression coverage for the plugin error field and for the already-honest shell.exec / cli.exec paths (none existed before). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A design doc for separating the Orchestration Core (reasoning/planning/memory) from the Action Runtime (execution/tool-use), grounded in the current Hermes gateway. Covers the decided terminology (OpenClaw = compat/reference only), the ExecutionTask/ExecutionResult contract, the phased migration (1a honest-status → 2 unified path → 3 SessionState + Brain-host → 4 intent/idempotency → 5 multi-runtime), cross-cutting rules (additive-first, test-first, observability, idempotency), and a decision log tracking what has landed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…n sites Phase 3 Step 1 (Central Brain + Action Runtime design, §11). The per-session state was an untyped dict at _sessions[sid] (~58 refs, ~30 keys, locks floating as dict entries). Introduce a SessionState type to anchor the decomposition. SessionState subclasses `dict`, so every existing session["key"] / .get / .setdefault / "k" in session call keeps EXACT dict semantics — zero handler churn. A safety analysis confirmed the gateway has no membership test, bare pop, or iteration that relies on the session value dict, and that absent keys must stay absent (a dict subclass gives that for free, so no field defaults need tuning — notably agent_ready stays absent on the eager path and personality keeps its config fallback). Typed @Property accessors (history, history_lock, running, agent, …) let handlers migrate to attribute access incrementally in later steps; the locks stay dict items until the subscript sites are converted. Wraps the two real creation literals (_init_session, session.create); test-constructed plain dicts still work since SessionState IS a dict. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SessionState type introduced and committed; note the next step (Step 2 lock-core methods). Also reflects the verified AIAgent instantiation-site count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…robe Generalizes the root cause behind the Gemma4-26B 64k pin: Ollama's /api/show reports the GGUF training max, which over-states the window a LOCAL server actually serves (the user-set Modelfile num_ctx) and silently truncates conversations once they outgrow it. Steps 2b and 5e now resolve local endpoints num_ctx-first (query_ollama_num_ctx) while hosted servers keep the GGUF-first probe (their users can't set num_ctx); discovered values persist under the same lmstudio cache exclusion the sibling steps use. The explicit Gemma pin stays as a tested guard above these steps. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening the picker live-probes every custom/local endpoint (keyless-local discovery in sections 3/4 and the live "custom" branch of provider_model_ids); with a server down each open burns the full HTTP timeout. Add a 30s in-process memo around fetch_api_models — keyed by endpoint + credential fingerprint and pinned to the exact function object so monkeypatched tests stay hermetic (plus an autouse clear in tests/hermes_cli). Empty results and exceptions are never memoized, so a down server still retries on the next open and the stale-beats-empty disk-cache semantics are untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…istry Pre-existing cross-file pollution: the dispatch-pool tests overwrite entries in the module-global _methods registry (slash.exec, session.compress, …) without restoring them, so four session.compress/snapshot tests in test_tui_gateway_server.py fail whenever both files run in one process. The server fixture now snapshots and restores _methods in place (same dict object) plus the directly-assigned module globals (_real_stdout, _hermes_home, _cfg_cache/_cfg_mtime/_cfg_path). Tests-only; both files also stay green alone. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claw3649
force-pushed
the
feat/action-runtime-honest-status
branch
from
June 10, 2026 07:08
8a4e0cb to
c962b39
Compare
Author
|
Addressed the self-review follow-ups in 3 new commits:
Verification: model-metadata suites 132 ✓ · picker suites baseline-exact (the 4 known live-ollama env failures only) ✓ · combined gateway run 273 passed ✓ · action_runtime + protocol 127 ✓ |
Phase 3 Step 2 (Central Brain + Action Runtime design, §11). The compaction snapshot + compare-and-swap, the turn-release finally, and the one-shot agent-build guard now live as SessionState methods — snapshot_history, commit_compaction, begin_turn, end_turn, build_once — each a verbatim lift of the server.py block it replaced (same lock, same .get defaults, same order). Five gateway sites migrated; sites whose critical sections interleave extra work with the CAS (the prompt.submit busy-gate + truncate, the poller gates, bare running-flag cleanups, the mid-turn history CAS that logs under the lock) stay deliberately inline to preserve atomicity, each noted in place. Test helpers that feed the migrated paths now construct SessionState (a dict subclass, so semantics are unchanged); 9 unit tests pin the method contracts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…esult wire
Phase 4 (Central Brain + Action Runtime design, §11): make the rich
ExecutionResult consumable.
- slash.exec accepts an optional task_id and echoes it additively (the wire is
byte-identical when absent — existing protocol tests pass unmodified).
- result_to_wire_rich renders the full contract: task_id, status, outputs,
error {type, retryable, message}, side_effects.
- New task.submit RPC: pilot scope intent="slash" only (reversible commands,
e.g. /model), other intents → 4030. The slash.exec body is extracted into a
shared _slash_exec_core returning the ExecutionResult; slash.exec renders the
legacy wire exactly as today, task.submit renders the rich wire.
- Idempotency: a locked in-process store (10-min TTL, 1024-entry cap) replays
the recorded result with replayed=true instead of re-executing; task_id
defaults to a fresh uuid4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-flight token Phase 4 race fix (from the PR review): selectModel applied optimistic switches with no in-flight correlation, so a slow switch A failing after a faster switch B succeeded rolled the store back to the pre-A model, clobbering B. Each selectModel call now captures a monotonically-increasing token; once a newer call starts, the older call is stale — a stale success commits nothing and a stale failure skips the store/cache rollback (the snapshot predates the newer switch) while keeping the failure toast so the user still learns their requested switch failed. refreshCurrentModel gains an optional staleness guard used by the persistGlobal paths; external callers are unaffected. The slash.exec request also carries an additive task_id for forward correlation; staleness is decided purely by the local token. Two race tests cover both interleavings. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion log Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author
|
Landed Phase 3 Step 2 and the Phase 4 pilot (4 new commits):
Verification: gateway suites 165 + 208 + combined 278 ✓ (only the known env-dependent browser test) · desktop race tests 4/4 + clean typecheck ✓ · all pre-existing protocol assertions unmodified (byte-compat oracle). |
…us surfacing Upstream moved the web model picker's chat path from slash.exec to config.set and added the expensive-model confirmation flow (pendingConfirm). Resolution keeps upstream's architecture and folds our honest-status work into it: all three modes (standalone onApply, gateway config.set, legacy onSubmit fallback) funnel through one awaited try/catch — a failed switch surfaces inline via submitError and the dialog stays open; a benign post-switch warning fires the new onWarning prop, which ChatSidebar toasts. The now-dead slash.exec onModelSubmit path is removed with upstream. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sh refactors, keep honest-status surfacing Upstream restructured executeSlashCommand into a slash-action table with a generic runExec path (and then made slash commands registry-driven in #42351); /model-with-args now rides runExec, so our result.error honest-status check lives there — covering every exec-style command instead of just the typed /model path. The refreshCurrentModel guard keeps BOTH protections: upstream's live-session-owns-the-footer guard and our in-flight staleness token. The model-controls test file unions upstream's two refreshCurrentModel tests with our four selectModel rollback/race tests (6/6 green under --environment jsdom). Desktop src/ suite shows the byte-identical failure set to pure origin/main (6 pre-existing); python gateway suites green. Note: this replaces an earlier broken push of the same resolution — a mid-merge stash for a baseline measurement silently dropped MERGE_HEAD, so 46a668be2 landed as a single-parent commit with partial upstream content. Reset and redone as a true merge; baselines are measured in detached worktrees from now on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
claw3649
force-pushed
the
feat/action-runtime-honest-status
branch
from
June 11, 2026 03:37
46a668b to
dae2efb
Compare
Author
|
Friendly ping for a review on this one 🙏 Quick triage signals to make this cheap to pick up:
Happy to split this further if the current size is the blocker — say the word and I'll carve it. |
This was referenced Jun 11, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Makes the Hermes gateway's exec contract honest (a failed action is always detectable, never looks like success), unifies the exec handlers behind a single Action Runtime schema, hardens model-picker + gateway infra reliability, and lands Phase 3 Step 1 (a typed
SessionState). All changes are additive on the wire — the three frontends (web / desktop / ui-tui) need no change.Grounded in a new architecture design doc (
docs/architecture/central-brain-openclaw.md) describing the Orchestration Core + Action Runtime split and its phased migration.What's in here (7 commits)
fix(model-picker)ollama rm'd models, preserve pinned subsets); always-live cache for custom/local with stale-beats-empty fallback; pin local Gemma 4 26B to its served 64knum_ctx; web/desktop picker surfaces rejected switches inline + rolls back the optimistic modelfix(gateway)feat(action-runtime)action_runtime/package: unifiedExecutionTask/ExecutionResultcontract + shell/cli/plugin/slash adapters, each with a byte-identicalto_wire(to_result(x)) == xround-triprefactor(gateway)shell.exec/cli.exec/slash.exec/ plugin) routed through the Action Runtime; structuredresult.erroron rejected/modelswitches and plugin failures (keys off_SlashSideEffect.kind, not message text); regression coverage that didn't exist beforedocs(architecture)refactor(gateway)SessionState(adictsubclass + typed accessors) at the two session creation sites; zero behavioral change, safety-analyzeddocsVerification
action_runtimeround-trip + contract: 13 ✓test_protocol.py, byte-compat oracle for the wire): 65 ✓test_tui_gateway_server.py): 208 ✓ (the 1 remaining failure —browser_manage— is a pre-existing environment case that fails onmaintoo)SessionStateunit: 4 ✓model_switch/models/model_metadata/ picker): green; the 2 priortest_user_providers_model_switchregressions are fixedScope / not included
ExecutionResultfields (status/error.retryable/side_effects/task_id) are produced but not yet consumed — consumers (idempotency, retry, the optimistic-update race fix) are Phase 4.history_lockturn-lifecycle + compaction CAS intoSessionStatemethods; then the Brain host) are deliberately left for a focused follow-up — they're concurrency-refactor work where rushing risks hard-to-detect bugs.🤖 Generated with Claude Code