f3ccab1f6067d188cdffb35b7a6ad46292a2ca2f
2924 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
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> |
||
|
|
81e484761f |
test(tools): keep unmocked package dependencies importable
The browser and Modal fixtures replaced agent/hermes_cli with packages whose empty search paths hid newly imported production modules. Point those fake package search paths at the real package directories while retaining their explicit collaborators. This exercises real secret-scope and subprocess helpers without weakening the lifecycle or snapshot assertions. |
||
|
|
e299626cc9 |
test(search): use neutral visible-content fixture text
Keep the exact hidden-directory and visible-hit assertions, but avoid an incidental process-killer token in the fixture query. The installed live-system pytest guard flattens argv and mistakes real skill plus a hermes temp path for a process-killer command. Neutral fixture text leaves that guard enabled and the search behavior unchanged. |
||
|
|
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 | ||
|
|
2f1085ec0b | fix(delegation): reject malformed child fallback chains | ||
|
|
0a53094897 |
fix: key the pin on delegation.model as well (model arm of #80450)
A delegation.model-only pin (provider inherited) took the unpinned column because pinned was keyed on override_provider alone, so the child inherited the parent chain and a mid-run failure could silently swap the pinned model. model at the call site is creds["model"] -- delegation.model config -- at both call sites, so keying on it adds no false pins. base_url-only pins already resolve to a provider override. Caught by review on the PR; two matrix tests added (model-only pin with absent and declared chains). |
||
|
|
72464503b4 |
fix(delegation): resolve the child fallback chain through the full pin x config matrix
Composes #80465's pin semantics with #80438/#80421's delegation.fallback_providers semantics (#65038) and settles the composition cell the three PRs leave undefined (#80450 map). _resolve_child_fallback_chain implements the whole decision table in one pure function: pinned children get no chain unless the delegation section declares one (a delegation-scoped chain honors both explicit intents; the silent drag in #80450 is specifically the PARENT chain substituting a pin); an explicit empty list disables fallback; absent config preserves historical parent inheritance exactly. Malformed declared values log and fall back PIN-AWARE — never the parent chain on a pinned child, so the config-error path cannot reintroduce the drag. Entries normalize through the canonical get_fallback_chain. The two existing inheritance tests are made hermetic against the real user config (as #80438 also did). |
||
|
|
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. |
||
|
|
b2c394bcad |
test: derive process_manage verbs from the handler table; widen aux-cancellation start-up bound
The schema-diet test froze the verb list (a change-detector); it now asserts the enum matches _SESSION_ACTIONS plus the by-name verbs. The cancellation harness gave a worker thread 1 s to reach its transport, which a loaded CI runner missed twice this week; the cancellation-latency assertion is unchanged, only the start-up wait is wider. |
||
|
|
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. |
||
|
|
284b303fa8 |
test: e2e conftest imports GatewayRunner at module level; elicitation test docstring matches the default thunk
Same cleanup as the previous commit, applied to the third file that carried the TYPE_CHECKING + in-function import pair. gateway.run imports none of the telegram/discord/ slack modules the conftest stubs, so import order is not a concern. test_feishu.py keeps its TYPE_CHECKING import on purpose (FeishuAdapter is gated on optional lark_oapi). |
||
|
|
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
(
|
||
|
|
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. |
||
|
|
633955408b | fix(tools): keep retained result reads off live status scans | ||
|
|
b72e373e23 | fix(tools): retain completed background process results across exit | ||
|
|
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> |
||
|
|
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> |
||
|
|
7a5fc1b2a9 | fix: remove automatic session JSON snapshots | ||
|
|
64950092d5 |
test: align delegated-child env tests with retained board routing
The descendant fence now keeps HERMES_KANBAN_DB/BOARD/WORKSPACE so a fenced child can still read the board it belongs to; only worker identity (TASK, RUN_ID, CLAIM_LOCK) is scrubbed. Three pre-existing tests still asserted the DB var was dropped and went red on CI. |
||
|
|
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. |
||
|
|
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. |
||
|
|
fe04d5b36d | test: preserve metadata and passthrough assertions after cap removal | ||
|
|
27f32bd50b | test: exercise output-cap removal across native and child surfaces | ||
|
|
fd3565deec | fix: remove dedicated user-facing output cap controls | ||
|
|
65f033a1a2 |
fix(execute-code): teach the working helper import contract
Slim adaptation of #83772 to the current schema and failure-hint table. Generated helpers are module exports on every execution path, not globals. Correct schema, recovery hints and CLI tip rather than injecting names or changing the execution boundary. Two registry-driven invariants reproduce both misleading instructions on main and execute the corrected guidance. Additional tool fix discovered during campaign #104904. Original diagnosis and correction: @yuzilongleif-collab (#83772). Co-authored-by: yuzilongleif-collab <235949691+yuzilongleif-collab@users.noreply.github.com> |
||
|
|
57c60f2e0c | fix: explain the launchctl registration restriction without inventing KeepAlive |