fb3446a281e4bddc733a04bf92a5ec5f0d6decc9
4398 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
fb5715c19f | fix: preserve subagent controls across attached session peers | ||
|
|
7befa11bf2 | fix: retain subagent control after live session reattachment | ||
|
|
866332bfb5 |
fix(relay): authorize send_message targets and surface egress declines (P5) (#99220)
* fix(relay): authorize send_message targets and surface egress declines
P5 of the relay egress-authorization workstream. The relay path
authenticated the SENDER but never authorized the DESTINATION, and the
gateway compounded it from both ends.
(a) send_message could silently name an arbitrary relay target. Its
`target` parameter is free-form ('platform:chat_id'), so a model could
name ANY chat id and the gateway would emit an outbound frame for it.
gateway/relay/egress.py adds an attestation floor: a relay-routed
destination must have a provenance this gateway can show -- the
operator's home channel, the channel directory, or its own gateway
session origins. Anything else is refused HERE, with a visible tool
error naming the target, before a frame is written. Non-relay platforms
and platforms served by a live native adapter in this process are
untouched (same precedence resolve_delivery_transport applies).
(b) Connector declines were swallowed into apparent successes. The
connector's egress floor answers an unauthorized destination with a
DEFINITE failure whose text is deliberately uniform (F-005). Several
relay lanes degrade a *transport drop* by design and were degrading an
*authorization refusal* the same way:
- _send_media returned None, sending the caller into
BasePlatformAdapter's text fallback -- a DIFFERENT op re-addressed at
the very chat the connector had just refused.
- _send_prompt returned None, so exec-approval / slash-confirm /
clarify reported "relay prompt op unavailable" (a wrong reason) and
ran their numbered-text fallbacks into the refused chat.
- task_card_stop discarded the error entirely.
- typing / delete / react / thread ops degraded silently at debug.
is_egress_decline() classifies THAT a decline happened (never why --
the uniform text is not parsed for reasons) and requires a definite,
non-ambiguous failure, so a lost-ack retry is still a transport
outcome. Lanes with an error-carrying contract now report the decline
verbatim; cosmetic bool/None lanes still degrade but log it at WARNING.
Advisory progress drops that legitimately degrade are unchanged: the
task_card send lane, the draft ambiguous/except branches, and every
transport-exception path keep their existing fail-open behaviour.
Tests: 21 mutations of the production source, all KILLED.
* fix(relay): authorize the RESOLVED target; declines must not fall back
Review round 1 (independently confirmed by a second reviewer) found three
blockers. Two are fixed here; the third (B-2, Telegram @username) is a policy
decision left open deliberately.
B-1 — THE FIX CAUSED THE OUTAGE IT PREVENTED (tools/send_message_tool.py)
The P5(a) guard ran ABOVE Slack user->DM resolution, so it authorized the
internal pseudo-id `_parse_target_ref` emits (`user_name:ben`, `user:U...`).
Provenances only ever hold RESOLVED conversation ids, so a fully attested DM
was compared as a handle against a set of `D...` ids and refused:
base slack:@ben SENT head(before) slack:@ben REFUSED
Every Slack DM by handle was broken. Moved the guard below resolution; it now
authorizes the destination that is actually sent to, and the refusal names the
resolved id. Position is load-bearing, so it is commented as such and pinned:
reverting the move turns exactly the four new cases red.
B-3 — A DECLINE IS NOT A LANE FAILURE (gateway/run.py)
`_approval_send_outcome` had only sent/failed/ambiguous, so a connector
decline collapsed into `failed` — which is the cue to run the plain-text
fallback into the chat the connector had just refused. The adapter fix in the
previous commit improved the error STRING while user-visible behaviour stayed
identical to base; the commit message overstated it. Fixed properly:
- new `declined` verdict, recognised via the shared `is_egress_decline`
contract (not string sniffing at the call site)
- exec-approval returns without the text fallback
- slash-confirm suppresses the text reply AND clears the registration, so a
card that never rendered cannot capture the user's next message
`send_clarify` was already correct (returns early inside the adapter).
MUTATIONS (production source; both directions)
classifier never returns 'declined' -> KILLED (4 cases)
ALL failures classified as 'declined' -> KILLED (2 cases)
guard moved back above Slack resolution -> KILLED (4 cases)
decline CODE changed (review M05) -> KILLED
marker match made case-sensitive (M10) -> KILLED
M05 was a tautology: the test asserted the imported constant against itself,
so changing the constant could not fail it. The wire contract is now pinned as
a literal, because the connector stamps that exact string and a one-sided
change is a silent cross-repo break.
REGRESSION CHECK: the 12 failures + 1 collection error in this test selection
are PRE-EXISTING cross-test contamination — the identical set fails at
|
||
|
|
5280fe9987 |
fix: cron and local DMs reach an open Desktop Bot Chat
Route local producers to durable owner ingress before attempting the unowned CLI lane. Preserve per-run/per-message IDs and receipt-first retry handling; never fall back after ambiguous admission. Report cron admission as queued, not completed or failed, in job status, the execution ledger and CLI/tool UX. Native isolated Electron validation reproduces SESSION_NOT_OWNED on main for both idle and busy owners. Fixed owner consumes idle cron, busy cron, local DM and mounted-chat cron exactly once, keeps its lease, yields to queued human input, and preserves the prior model-request prefix and tool schema. Inference alone used a deterministic loopback wire stub; no paid model call. |
||
|
|
db96c8ced7 |
fix: admit Bot Chat deliveries through the live session owner
Adapt FalconOrtiz's owner-mailbox proposal from #101564 onto the current notification poller and topical modules. The durable mailbox is cross-process ingress only: the existing owner admits its normal prompt turn after the current turn and human FIFO clear. Retain immutable receipts, stable admission identities, capability and lease fencing, compression lineage, and disable blind recovery replay of imported turns. The original stale server hunks and expiring receipt protocol were rebuilt rather than cherry-picked: the current facade decomposition and durable busy admission contract differ. Credit the earlier owner-mailbox work in #100544 and durable producer work in #100319. Co-authored-by: fangliquanflq <fangliquan@qq.com> Co-authored-by: 686f6c61 <github@00b.tech> |
||
|
|
6178e9f4ee | fix(approvals): honor GNU env split escapes and argv0 operands | ||
|
|
50617d1c75 | fix(approvals): preserve env argv and shell comment boundaries | ||
|
|
58faa10134 |
fix(approvals): match denied executable paths behind shell prefixes
Adapt the command-position, bounded-candidate and launcher-option work from embwl0x's #76063 to the current detection owner, then add executable basename projection from Rohith Pariki's #104338. Parse raw quote state before applying existing text normalization so quoted arguments do not become commands. Cover shell payloads and literal env split-string carriers, retain path-specific rules and whole-command globs, and document the supported normalization rather than claiming an OS capability sandbox. Related: #104308, #76037, #76063, #104338, #78521, #86711. No automatic closing directives: the older carriers also contain broader case syntax and git-option work not included here. Co-authored-by: embwl0x <embwl0x@users.noreply.github.com> Co-authored-by: Rohith Pariki <rohithpariki@gmail.com> |
||
|
|
4810074d73 | fix: retain completions until explicit adapter admission | ||
|
|
3b7ff435fd |
fix(kanban): preserve durable origins for worker-created tasks
Carry the owning task's notification subscriptions independently of dependency edges, within the creation transaction. Prefer its durable session over worker and request-local sessions while preserving explicit overrides. Cover worker CLI create and built-in decomposition, and retain conversation route anchors. Auto-subscribe no longer upgrades an inherited passive subscription. Slim adaptation of Christopher-Schulze's session-precedence fix in #85687, expanded to durable subscription provenance and sibling creation paths. Related: #85575, #85687 Validation: strict RED/GREEN (7 failing cases before; 7 passing after), then 58 Kanban test files: 383 passed, 2 skipped. Real dispatcher-spawn subprocess probe covers direct, linked, unlinked, explicit-session, worker CLI, built-in children and a plain CLI negative control, with recording transport only. Co-authored-by: Christopher <210261288+Christopher-Schulze@users.noreply.github.com> |
||
|
|
16fe50b1a2 |
refactor(gateway): inline the user-bus adoption gate at the run_gateway call site
Drop the 3-line facade wrapper (hermes_cli/gateway.py is already 3x the facade threshold) and call the existing _ensure_user_systemd_env() directly under `is_linux() and INVOCATION_ID` — the same Linux gate the process_registry seam uses, instead of os.name == "posix". The fail-closed test now targets _ensure_user_systemd_env() itself. Hedge the scope-unavailable error text: the probe also returns False when systemd-run is missing or times out, so the D-Bus diagnosis is the usual cause, not the only one. |
||
|
|
802f0f97ad |
fix(gateway): adopt the user D-Bus session when systemd starts the gateway
A system-level unit (/etc/systemd/system, User=<someone>) is exec'd with neither XDG_RUNTIME_DIR nor DBUS_SESSION_BUS_ADDRESS, and a process environment is fixed at exec time. 'systemd-run --user --scope' therefore fails for the whole lifetime of that gateway even after the user manager is up and /run/user/<uid>/bus is reachable. That is the seam every restart-safe worker crosses (restart_safe_gateway_child_argv), and it fails closed by design — so on headless systemd installs every agent-driven cron job and every Kanban dispatch died at launch, ~26ms in, with nothing but 'error' on the job row. _ensure_user_systemd_env() already derives both values from our own uid and adopts them only when the runtime dir is really ours and the socket really exists; it was just wired exclusively to the systemctl management paths, never to the gateway's own boot. Call it from run_gateway() — the single in-process boot every entry point goes through — so the adoption precedes every worker-environment snapshot (cron builds its env after the scope check, Kanban before it, so fixing this at the dispatch seam would only fix one of them). The fail-closed posture is unchanged: with no user manager at all the probe still reports unavailable and dispatch still refuses. That refusal now names the remedy in the message the operator actually reads (it is stored as the cron execution's error), instead of only the symptom. Fixes #104893 |
||
|
|
99e1c16868 |
test(tools): pin the Git Bash probe's detached stdin in the existing probe test
The new standalone test file duplicated TestGitBashExternalProgramProbe's harness; one assertion on the recorded kwargs covers the #78820 contract. The sibling ASLR-probe assertion is dropped (unchanged code, already green on main). Comment trimmed to the WHY. |
||
|
|
869432301b |
fix(tools): detach stdin in the Windows Git Bash probe (#78820)
`_bash_starts()` in tools/environments/local_gitbash_probe.py ran bash.exe
with capture_output=True but no stdin=, so the MSYS2 child inherited the
TUI gateway's stdin pipe. The MSYS2 runtime switches that shared pipe to
PIPE_NOWAIT; the gateway's next sys.stdin.readline() then fails with
ERROR_NO_DATA, which the CRT maps to OSError(EINVAL), and the gateway
exits with code 1 ("gateway exited") on the first terminal call.
The sibling probe _mandatory_aslr_enabled() already passes
stdin=subprocess.DEVNULL; this brings _bash_starts() in line with it.
Add tests/tools/test_gitbash_probe_stdin.py asserting both probes forward
stdin=DEVNULL to subprocess.run (fails on the pre-fix source).
|
||
|
|
1e24a8de39 |
refactor(delegation): resolve the child fallback chain through the canonical normalizer
_resolve_child_fallback_chain re-implemented hermes_cli.fallback_config's entry validation to log per-index warnings, wrapped a pure function in try/except, and carried two names (routing_cfg / fallback_cfg) for one argument. It now delegates to get_fallback_chain() and keeps the single warning for "no usable routes"; the facade re-export is dropped (import from the defining module). Tests collapse to the parametrized decision table plus the pin-derivation, real-config-loader, review-ownership and activation invariants (22 -> 20 cases, 417 -> ~230 lines). |
||
|
|
c47bf78d68 | fix(delegation): keep child routes and fallback policy together | ||
|
|
869228cab4 |
feat(delegation): surface process accounting as top-level process_notes in the sync result; add live stress harness
Stress run with a real orchestrator subagent showed the per-task keys buried in results[] were not relayed upward; the same prose lines now sit at the top of the sync delegate_task result. evals/subagent_process_handoff/ stress_handoff_live.py runs 12 real parent+child scenarios (handoff, orphan, unread, clean read, cap, exited refusal, mixed fan-out, parent controlling the inherited process, sibling theft incl. adversarial, nested orchestrator, 5-way burst) and scores them against runtime accounting, never model prose. |
||
|
|
ef9239571d |
feat(delegation): report a child's exited-but-unread notify processes to the parent
A process that finishes while the child is alive needs no handoff, but if the child never polls/waits/logs it, the result vanished: the completion notice is suppressed in the parent and the child's summary never mentions it. Finalization now attaches exit code + output tail as unread_completions, rendered in the parent's delegation notice. |
||
|
|
3c0d90e8ef |
feat(delegation): subagents hand background processes to the parent; leftovers are named, not trusted
A child's background processes are killed at its teardown and their notify_on_complete notices are suppressed in the parent, yet the child's terminal result still said `notify_on_complete: true` and the parent's delegation notice said nothing about processes left behind. Orchestrators believed "CI watcher running" and waited on a completion that could never arrive (recurring in the Sep 7 campaign sessions). - process_manage(action="handoff", session_id, data="<purpose>"), children only: process_registry.transfer_ownership flips owner_task_id/task_id/ session_key to the parent under the registry lock, so the completion is stamped with the parent's owner at exit, passes the parent's sa- filter, and is reaped by the parent, not the child. Cap 3 per child; an exited, foreign, or non-child request is a tool error. The purpose rides the event as handoff_note and renders in the parent's notice. - Child terminal(background=True, notify=True) now returns notify_on_complete=false plus a note: wait, kill, or hand off. - _ChildRun.account_background_processes records handed_off_processes and orphaned_processes on the result before cleanup kills the leftovers; the parent's delegation block renders both. |
||
|
|
2f1609a86c |
refactor: sms AIOHTTP_AVAILABLE flag; ElicitationHandler call_context defaults to a no-op thunk; drop stale TYPE_CHECKING/type-ignore in two tests
Self-review follow-ups on the F821 sweep: - plugins/platforms/sms/adapter.py: the optional-import block now sets AIOHTTP_AVAILABLE like the homeassistant / webhook / whatsapp_cloud adapters, and both call sites test the flag. Removes the `if not aiohttp is not None:` double negation left by inlining `_aiohttp_available()`. - tools/mcp_tool_sampling.py: `call_context` defaults to `lambda: None` so the use site is a single call instead of an Optional guard; the only None caller was a test. The `from __future__ import annotations` was noise (`Context` is a runtime import). Comment names the actual cycle (mcp_tool_server_run imports this module). - gateway/platforms/helpers.py: drop the `from __future__ import annotations` — the only MessageEvent annotations are attribute-target locals, which are never evaluated. - tests/gateway/test_telegram_audio_vs_voice.py, test_video_context_note.py: module-level `from gateway.run import GatewayRunner` like the ~100 sibling files; the TYPE_CHECKING block + `# type: ignore[name-defined]` were contradicting each other. (tests/e2e/conftest.py and test_feishu.py keep TYPE_CHECKING deliberately: they stub telegram/discord before importing, and FeishuAdapter is gated on optional lark_oapi.) Mutation check: neutralising the thunk read (`captured = None`) fails test_captured_context_is_replayed_in_consent_call; restored → 14/14 green. ty on the three touched production files vs origin/main: 0 new, 6 resolved. |
||
|
|
ab2f4602de |
refactor: MessageEvent to gateway/platforms/event.py; ElicitationHandler takes a call_context thunk
Breaks the two import cycles that forced Protocol stand-ins in the F821 sweep, so the two sites now name the real types. gateway/platforms/event.py (new leaf): MessageType, ProcessingOutcome, MessageEvent moved out of base.py verbatim. Their only dependency is gateway.session.SessionSource; base.py imported helpers.py at module level, so helpers could not name MessageEvent. Now TextBatchAggregator is typed by the real MessageEvent. 249 importers repointed (`from gateway.platforms.base import` -> `.event`, preserving each import's layout); gateway.platforms.__init__ re-exports from .event. The three revert-scheduled PLUGIN-COMPAT pointers that named these symbols (gateway.slash_commands → MessageType, dingtalk → MessageType, photon → ProcessingOutcome) and their COMPAT_MANIFEST rows now target gateway.platforms.event. Docs updated: ADDING_A_PLATFORM.md, adding-platform-adapters.md (en + zh-Hans). tools/mcp_tool_sampling.py: ElicitationHandler no longer holds a back-reference to its MCPServerTask (mcp_tool imports sampling, so the task type cannot be named there). It only ever read owner._pending_call_context, so it takes `call_context: Callable[[], Context | None]` and MCPServerTask passes `lambda: self._pending_call_context`. The consent call is one `functools.partial`, run directly or inside the captured Context. ty on the 11 touched production files vs origin/main: 0 new diagnostics, 14 resolved. (The one `source: SessionSource = None` diagnostic moves with the class; typing it Optional exposes ~60 unguarded call sites — separate follow-up.) Tests: tests/gateway + tests/plugins + tests/tools + touched files, 18,235 passed; the 31 failures reproduce identically on origin/main (macOS /private/tmp, systemd socket, long-path fixtures, live-service tests). |
||
|
|
c5ff900761 |
fix: resolve the 33 F821 undefined names outside tui_gateway / feishu / godmode
Sweep of `ruff check . --select F821 --target-version py311`: 2,234 hits. 2,201 are left
alone on purpose: tui_gateway (2,169; bind_module rebinds bodies onto server.py globals,
all names verified to resolve there), the Feishu adapter (27; globals().update() SDK
binding) and the godmode script (5; dead standalone script). The other 33 were all
genuine defects. No lint config change; no TYPE_CHECKING escape hatches — every
annotation names a real, imported type; ty on the touched files: 0 new diagnostics.
- gateway/slash_commands.py: HISTORY_UNREADABLE never imported after #102117
→ NameError on the /btw error branch (same one-liner as #102952).
- gateway/platforms/whatsapp_common.py: `-> Path` return annotation with no Path
import (the body uses `_Path`). Never raised at runtime thanks to
`from __future__ import annotations`, but `typing.get_type_hints()` and ty
both fail on it.
- gateway/run.py: ActivityProvenance imported at module level
(agent.session_activity has no gateway deps); stringly annotation and the
lazy in-function import are gone.
- tools/patch_parser.py: PatchResult imported at module level; real return
annotation. The "avoid circular import" lazy import guarded a cycle that
does not exist (file_operations_common never imports patch_parser).
- gateway/platforms/helpers.py: base.py imports helpers at module level, so
MessageEvent cannot be named here; TextBatchAggregator only reads .text and
.source, so it is typed by a BatchableEvent Protocol that MessageEvent
satisfies structurally.
- tools/mcp_tool_sampling.py: mcp_tool imports this module, so MCPServerTask
cannot be named here; ElicitationHandler only reads
owner._pending_call_context, typed by an ElicitationOwner Protocol.
- plugins/platforms/sms/adapter.py: aiohttp is an optional dep ([messaging] extra) →
module-level try/except ImportError binding `aiohttp = web = None`, the pattern the
homeassistant / webhook / whatsapp_cloud adapters already use. Retires three lazy
in-function imports and the `_aiohttp_available()` wrapper; `_handle_webhook` typed
`web.Request -> web.Response`.
- plugins/platforms/teams/summary_writer.py: plain module-level `import httpx` — httpx is a
hard core dependency (pyproject `httpx[socks]==0.28.1`), so the lazy import and the
"imported on every CLI start" docstring premise were both wrong (plugin discovery never
imports this module; it is reached only via the Teams adapter / meeting pipeline).
Tests:
- tests/hermes_cli/test_config.py: a test body orphaned by the wave-1 prune
(
|
||
|
|
39ed610f8c |
feat(cron): create paused jobs without a scheduling race
Persist paused state, timestamp, reason and no first trigger in the original locked creation write. Forward the same boolean contract across CLI, tool, gateway API and dashboard API, validating at the store boundary. Preserve explicit operator force-run behavior and normal enabled creation. The live CLI probe also caught the command shim dropping failure return codes; forward them so invalid creation reports exit 1 rather than success. Credit earlier atomic-creation work in #78935 and #94952 and the focused implementation in #104578. The broader manifest staging layer is not imported. Co-authored-by: Konstantin Khlopkov <konstantin.khlopkov93@gmail.com> Co-authored-by: Chloé DuPont <321112755+misschloedupont@users.noreply.github.com> |
||
|
|
9af6e9fc8b |
fix: coalesce interactive completion backlogs without losing identity
Based on the earliest batching proposal by BrunoBza (#104686) and JoaoMarcos44 structured correction (#104703). Slim redo into topical siblings and shared TUI poller/post-turn routing. Reported by Xipong (#104671). Local shell-to-loopback probes demonstrate 12 to 1 turn dispatches; suites await the campaign test lock. Co-authored-by: joaomarcos <joaomarcosdias444@gmail.com> |
||
|
|
1735ccde44 | fix(tools): always redact durable process receipts | ||
|
|
0522ae934e | fix(tools): retain producer profile scope in process readers | ||
|
|
fbed1d4584 |
fix(tools): keep retained terminal results scoped to their owner
Capture the durable parent session before output readers start, including CLI and non-notifying spawns. Require that parent or its compression continuation for retained reads; exact and prefix handles alone do not authorize access. Live Linux terminal/one-shot linger/fresh-reader A/B: base loses results; updated owner recovers both streams and exit 7. Unbound, foreign session, delegated child, and other profile cannot recover the receipt. No notifications are replayed. Full tools suite is queued behind the campaign test lock. Follow-up to contributor salvage #104805 for #104511. |
||
|
|
1770825481 | fix(tools): register readers atomically with completion | ||
|
|
633955408b | fix(tools): keep retained result reads off live status scans | ||
|
|
b72e373e23 | fix(tools): retain completed background process results across exit | ||
|
|
5695ebc40f | refactor(tools): extract process checkpoint persistence | ||
|
|
f94307a7f7 |
fix: ignore malformed MCP OAuth metadata caches
Guard device metadata subtype selection with a dictionary check so valid non-object JSON reaches the existing validation-and-ignore path. Preserve null handling, cache contents, and normal/device metadata cold-load types. Extend the existing corrupt-cache invariant rather than adding test functions. Live filesystem A/B reproduces list/string/number/boolean AttributeError on the prior head and clean ignore after this change. Nine CLI OAuth wire scenarios, browser S256 and profile-scoped storage controls pass locally. |
||
|
|
965f18b02f | fix: restore prior device OAuth state if persistence fails | ||
|
|
f5afe8bd40 |
feat: authorize MCP servers with device codes from the CLI
Add explicit RFC 8628 device login and oauth.flow selection while keeping browser PKCE and the SDK runtime refresh path. Reuse issuer/resource validation, configured client authentication and profile-scoped storage. Only persist an approved, validated grant; never echo endpoint error bodies. Slim redo of #104752 by @wjorgensen, replacing duplicate HTTP/storage wrappers with the existing SDK and two real-wire invariant tests. Refs #104742 Co-authored-by: Wes Hermes <weshermes@Wess-Mac-mini.localdomain> |
||
|
|
e1a161538a |
fix(cron): make failed runs diagnosable without verbose delivery errors
Persist a redacted chained traceback in the private run output and expose redacted last_error in tool and slash listings, including historical errors. Keep the run_job concise error return unchanged for delivery classification. Slim redo of liuhao1024's earliest #104545; adds forced redaction and keeps formatting in a topical sibling. Local SDK/socket A/B verifies diagnosis visibility plus healthy-script, clearing, and private-file controls. Canonical tests queued under the campaign lock at commit time. Co-authored-by: liuhao1024 <sunsky.lau@gmail.com> |
||
|
|
549e6aab38 |
fix(bot-mode): preserve refusal reasons across local delivery
Emit the one-shot reason marker outside the CLI facade; parse whole codes before falling back to legacy prose. Explicit coordination and unknown codes cannot be labeled target_busy. Fixes #104784 Co-authored-by: William Echo <2054936695@qq.com> |
||
|
|
8aaf1aca62 |
fix(schemas): preserve required intent without invalid boolean flags
Normalize boolean required only at schema positions; lift true property flags into parent arrays. Preserve literal default/const/extension data. Fixes #104796 Inspired by #104831 and the lifting proposal by @AdJIa. |
||
|
|
7876d183c9 |
fix(approval): recover legacy list values without character grants
Recover legacy stringified lists with a warning. Reject malformed shapes and nonstring members without admitting approvals or rewriting user config on read. Fixes #104779 Co-authored-by: liuhao1024 <sunsky.lau@gmail.com> |
||
|
|
5904c7a395 |
fix(threats): keep unrelated role prose in context files
Salvage the bounded target-slot design from #104617, using mandatory word separators to avoid ambiguous repeated matches. Preserve long payload detection and execution-verb boundaries. Replace the three candidate tests with two context-loader invariants and document the heuristic's limits. Fixes #104609 Co-authored-by: Konstantin Khlopkov <konstantin.khlopkov93@gmail.com> |
||
|
|
b578261584 |
fix: keep Kanban worker scope out of descendant processes
Carry the existing write fence across Hermes-owned spawn boundaries without dropping board routing or changing credential policy. Grant dispatcher and managed tool runtimes explicit task scope; align CLI task mutations with tools. Verify real shell/CLI descendants, dispatcher startup, and supervised stdio transport against isolated SQLite boards. This is cooperative runtime scoping, not OS confinement. Refs #103974, #104058, #104904 |
||
|
|
9745a7f0f1 | fix(terminal): show sudo password prompts for paths and env prefixes | ||
|
|
727c2d7525 | fix: accept the live ClawHub version-list response shape | ||
|
|
76de6ec5a8 | fix: retain ClawHub owner through version and bundle requests | ||
|
|
f986a2b103 |
fix(skills): pass the ClawHub owner hint as ?owner= so ambiguous slugs resolve
ClawHub's detail endpoint now answers a slug claimed by multiple owners with 409 AMBIGUOUS_SKILL_SLUG; the bare GET in _skill_detail returned None for every such slug, so 'skills install clawhub/@owner/slug' (and the owner/skills/slug URL form) failed at fetch time even though the requester already knew the owner (#104117). - _skill_detail forwards expected_owner as the ?owner= query param on the detail GET (params already flows through _get_json's **kwargs). - _parse_identifier also accepts the clawhub/@owner/slug combination: the @ surfaces only after the clawhub/ prefix is stripped, so the had_at check now re-runs on the stripped form. GitHub-style owner/repo/skill paths stay rejected. |
||
|
|
05315a6f26 | fix: keep pagination signature inspection local and preserve exact decoder error | ||
|
|
55c223e247 |
fix(mcp): probe the list signature instead of masking its TypeError (#104150)
_paginate_full_list wrapped the paginated list call in try/except TypeError to detect the mcp 1.x calling convention. The same except also caught TypeErrors raised INSIDE the modern list call — e.g. a server response decode failure — and retried with the legacy cursor= keyword, replacing the real error with a misleading 'unexpected keyword argument cursor' and making genuine MCP pagination failures undiagnosable. Probe list_method's signature instead (_list_method_accepts_params): the legacy cursor= fallback fires only when the method genuinely doesn't accept the mcp 2.0 params= keyword (or takes **kwargs), so a TypeError from inside the list call propagates to the caller. Regression tests: the decode TypeError surfaces and the legacy retry doesn't run; a genuinely 1.x-shaped method keeps using the cursor fallback. |
||
|
|
ae9cd7073e | fix(delegation): keep the 'wait or poll' contract token in the tool description | ||
|
|
c89f3b8800 |
fix(delegation): one completion per call by default; queued units no longer stalled; tell the model results land between turns
Three orchestrator failures traced through the Sep 7 gpt-6-astra campaign sessions: 1. delegation.independent_completions (new, default false). #104299 made every ungrouped task its own completion message, so a 15-task call woke the orchestrator up to 15 times; one chain received 132 notices and answered 130 of them with "already incorporated". A multi-task call now returns as ONE consolidated message unless the flag is on; `group` is inert until then. 2. Queued units were killed before they started. Units of one call share a pool slot but the executor was still sized by slots, so with 15 units live a new unit queued behind a full pool; the stale monitor's clock ran from dispatch, interrupted it at 450 s, and the child exited `interrupted 0.02s` when its thread finally came up (13 such lanes in one session). The executor now grows to the number of live units and the stall clock arms when the runner actually starts. 3. The tool text said "do not wait or poll — just continue" without saying that completions are delivered only BETWEEN turns. A model that never ends its turn (one 203-minute turn, 717 API calls) never received 40 finished results. Tool description, dispatch note and completion header now say to finish independent work, give a one-line status, and end the turn. |
||
|
|
14b9b93668 |
fix(deps): sync LAZY_DEPS platform.discord brotlicffi pin to 1.2.0.2
The previous commit bumped pyproject.toml and uv.lock but missed the
LAZY_DEPS exact pin for platform.discord, so
test_pyproject_pins_match_lazy_deps_pins and
test_every_lazy_deps_exact_pin_matches_uv_lock fail with
{'brotlicffi': {'platform.discord': {'lazy_pin': '1.2.0.1', 'uv_lock': ['1.2.0.2']}}}.
Update the third registration site to keep all three in lockstep.
|
||
|
|
fd3565deec | fix: remove dedicated user-facing output cap controls |