Commit Graph

4079 Commits

Author SHA1 Message Date
Peter Steinberger
133ca4b469 feat(skills): diagnose skill_workshop hidden by tool policy (#100654)
* feat(skills): diagnose skill_workshop hidden by tool policy (#87570)

Workshop can be enabled and auto-capturing while tools.profile hides the
skill_workshop tool; every inspection surface looked healthy. plugins
inspect and openclaw doctor now name the excluding policy layer (global/
agent/provider profile, allowlist, denylist) and the exact alsoAllow
grant to add, via a shared resolveSkillWorkshopToolPolicyAvailability
helper that /learn's guard now reuses instead of composing policy
itself. Diagnosis only; no policy behavior change.

* ci: retrigger
2026-07-06 07:21:08 +01:00
Peter Steinberger
dd972b8bf7 fix: replies fail when memory flush is exhausted (#100618)
* fix(reply): recover from memory flush exhaustion

Co-authored-by: 忻役 <xinyi@mininglamp.com>

Co-authored-by: Jerry-Xin <jerryxin0@gmail.com>

* test(reply): remove unused memory flush result

---------

Co-authored-by: Jerry-Xin <jerryxin0@gmail.com>
2026-07-06 06:41:16 +01:00
pick-cat
f296dc4e68 fix(sessions): persist sender metadata in user turn transcript JSONL (#90552)
* fix(sessions): persist sender metadata in user turn transcript JSONL

Thread senderId/senderName/senderUsername/senderE164 from the channel
inbound context into the persisted user-turn transcript message so that
group chat session JSONL records include __openclaw sender identity.

Ref #90531

* fix(sessions): exclude senderE164 from persisted transcript for privacy

Remove phone-number field from the persisted __openclaw sender envelope,
keeping only senderId, senderName, and senderUsername. Privacy-sensitive
E.164 metadata can be added back by maintainers if needed.

Ref #90531

* fix(infra): spread base fields in applyExecPolicyLayer return values

The `as TBase & ExecPolicyLayer` casts failed because the returned
objects did not spread `...base`, losing generic TBase fields.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(infra): use ...base spread instead of ...baseWithoutMode

The baseWithoutMode destructuring produces Omit<TBase, "mode"> which is
not assignable to TBase & ExecPolicyLayer when the as-cast is removed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(infra): preserve baseWithoutMode in second applyExecPolicyLayer branch

The second branch (security/ask override without a mode change) must
exclude base.mode from the spread so that mode is not leaked into results
when only security or ask fields are being overridden.

Without this fix, the spread ...base carries mode through to the returned
object, breaking callers that expect applyExecPolicyLayer to clear stale
mode when applying explicit security/ask policy fields.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(sessions): scope persisted sender attribution

* fix(sessions): preserve sender metadata through hooks

* refactor(sessions): keep sender metadata path lean

* fix(sessions): preserve sender metadata in runtime writes

* fix(sessions): preserve queued sender attribution

* test(sessions): use complete message fixtures

* refactor(sessions): rely on narrowed user message type

* test(sessions): use shared temp directory helper

* test(sessions): align sender metadata assertions

* fix(sessions): honor sender metadata redaction hooks

* test(agents): use automatic temp cleanup

* test(sessions): cover queued turn provenance

* test(auto-reply): expect room sender metadata

* refactor(sessions): isolate queued transcript context

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 05:32:19 +01:00
Peter Steinberger
bf04d049fa feat(goals): keep active session goals in per-turn context + continuance QA scenarios (#100468)
* feat(goals): keep active session goals in per-turn context (#100409)

Active goals now inject one bounded user-role context line on every
non-heartbeat turn, refreshed at queued/interrupt admission via
provenance-tracked generated lines so operator stops take effect
immediately. Adds qa/scenarios/goals continuance scenarios and
goals.* coverage IDs.

* docs: regenerate docs map for goal context section

* fix(qa): use requiredChannelDriver instead of execution.channel pin in goal scenarios
2026-07-06 04:55:10 +01:00
Peter Steinberger
62ecc2306c feat(commands): add /learn to draft skills from recent work (#100442)
* feat(commands): add /learn to draft skills from recent work (#100408)

/learn rewrites the turn into a standards-guided Skill Workshop authoring
instruction: the agent gathers named sources (or distills the current
conversation) and files ONE pending skill proposal via skill_workshop.
Approval flow unchanged; sandboxed/tool-restricted agents get a clear
unavailable reply. Extracts the harness OpenClaw-tools predicate into
shared helpers and reserves the command name against plugin shadowing.

* docs: regenerate docs map for /learn section
2026-07-06 04:54:22 +01:00
Peter Steinberger
07e2d633cc feat: show auto-detected provider plans and billing (#100520)
* feat(providers): auto-discover usage billing

* feat(ui): show provider plans and billing

* fix(ui): translate provider billing labels
2026-07-06 04:53:09 +01:00
Gorkem Erdogan
39916560e4 improve(auto-reply): render chat history since last reply as per-message prose (#100366)
* fix(auto-reply): render chat history since last reply as per-message prose

The inbound chat-history block dumped batched history as a raw JSON array, which models read poorly compared to the chat-window block's per-message prose. Reuse the existing formatChatWindowMessage renderer for history entries so both blocks share one shape, keep the untrusted framing label, and keep media rendered as a bare content-type tag so local paths and URLs stay redacted. Teach the metadata stripper to consume the new prose block form.

* fix(auto-reply): preserve every media content type in chat-history prose

The prose chat-history renderer only forwarded the first attachment's
content type per history message, dropping the rest for entries with
multiple media items. Join all bounded content types instead, and
regenerate the prompt snapshot fixture this changes.

* test(qa-lab): accept prose pending history

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 03:59:29 +01:00
brokemac79
49ae7ec065 [codex] Fail closed pair slash command routing (#98262)
* fix: keep pair qr from widening gateway bind

* fix: honor plugin activation for slash reservations

* fix: authorize spaced slash commands

* fix: keep reserved commands out of manifest reservations

* fix: avoid manifest fail-closed for declined commands

* fix: gate manifest command fallback by auth

* fix: keep runtime command probe internal

* fix: scope spaced slash authorization

* fix: route spaced plugin slash commands

* docs: note spaced plugin command routing

* docs: note spaced plugin command routing

* docs: split command routing changelog follow-up

---------

Co-authored-by: brokemac79 <255583030+brokemac79@users.noreply.github.com>
Co-authored-by: Peter Steinberger <58493+steipete@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-06 02:55:58 +01:00
Peter Steinberger
2c31af1364 refactor(auto-reply): harden empty reply delivery (#100474)
* refactor: centralize visible delivery evidence

* fix: enforce queued delivery policy

* fix: normalize queued fallback delivery

* fix: confirm queued notice delivery

* fix: track direct compaction delivery

* test: align compaction retry fallback

* test: cover terminal compaction delivery

* fix: normalize followup payload visibility

* fix: prepare empty direct reply fallbacks

* fix: align block delivery visibility

* fix: distinguish progress from reply delivery

* test: assert recovery after compaction progress

* fix: retain queued reply ownership through recovery

* fix(auto-reply): require visible messaging evidence

* refactor(auto-reply): remove duplicate empty-reply paths

* docs(changelog): clarify empty-reply guarantees

* fix(auto-reply): preserve terminal reply semantics

* test(auto-reply): type silent followup cases

* fix(auto-reply): preserve group silence policy

* fix(agents): retain rich delivery evidence
2026-07-06 01:18:27 +01:00
Peter Steinberger
f0093b7020 fix(auto-reply): surface empty interactive completions (#100456)
* fix(auto-reply): surface empty interactive completions

Co-authored-by: mushuiyu886 <yang.haoyu@xydigit.com>

* chore(changelog): defer empty-reply release note

---------

Co-authored-by: mushuiyu886 <yang.haoyu@xydigit.com>
2026-07-05 22:22:20 +01:00
Moeed Ahmed
04857c78ee fix: surface terminal agent run failures (#99304)
* fix: surface empty terminal run failures

Co-authored-by: Moeed Ahmed <5780040+moeedahmed@users.noreply.github.com>

* docs: note terminal failure replies

* fix: normalize terminal failure replies

* fix: preserve terminal failure reply threading

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Moeed Ahmed <5780040+moeedahmed@users.noreply.github.com>
2026-07-05 12:46:56 -07:00
Peter Steinberger
c757675f34 improve: keep isolated tests under one second (#100019)
* test: speed up isolated test suite

* test: finish isolated latency cleanup

* test: eliminate remaining isolated latency spikes

* test: remove final isolated timing outliers

* test: bound full-suite tooling processes

* test: bound native test process lifetime

* test: warm isolated runtime suites

* test: eliminate final isolated timing outliers

* test: fix isolated timing fixture types

* test: make timeout cleanup timing deterministic

* test: pin media manifests to source checkout

* test: isolate provider manifest contracts

* test: eliminate residual isolated timing spikes

* test: restore final isolated timing fixes

* test: eliminate remaining isolated timing spikes

* test: warm Zalo lifecycle imports

* test: keep isolated suites below one second

* test: use readable browser response fixtures
2026-07-05 10:57:19 -07:00
Peter Steinberger
ba821e7220 fix(models): resolve provider-qualified aliases (#100209)
* fix(models): resolve provider-qualified aliases

Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>

* docs(changelog): defer TUI batch entries

---------

Co-authored-by: Sahil Satralkar <62758655+sahilsatralkar@users.noreply.github.com>
2026-07-05 06:44:54 -07:00
Peter Steinberger
deac98eb72 fix: harden small runtime and installer edge cases (#100258)
* fix(media): preserve dollar sequences in transcript echoes

Co-authored-by: uditDewan <udit.dewan21@gmail.com>

* fix(mcp): catch rejected gateway event handlers

Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>

* fix(auto-reply): support punctuated silent token prefixes

Co-authored-by: simon-w <weng.qimeng@xydigit.com>

* fix(discord): keep voice log previews UTF-16 safe

Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>

* fix(clawrouter): bound usage response reads

Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>

* fix(browser): bound CDP JSON response reads

Co-authored-by: hailory <hailory@xydigit.com>

* fix(status): include current time in session status

Co-authored-by: connermo <conner.mo@gmail.com>

* fix(installer): require Arch detection before pacman

Co-authored-by: Iliya Abolghasemi <gfaerny@gmail.com>

* fix(apps): default TLS gateway deep links to port 443

Co-authored-by: ben.li <li.yang6@xydigit.com>

* fix(infra): keep Undici terminated exceptions nonfatal

Co-authored-by: harjoth <harjoth.khara@gmail.com>

* fix(auto-reply): avoid Unicode-unsafe token spread

---------

Co-authored-by: uditDewan <udit.dewan21@gmail.com>
Co-authored-by: 陈宪彪0668000387 <chen.xianbiao@xydigit.com>
Co-authored-by: simon-w <weng.qimeng@xydigit.com>
Co-authored-by: sunlit-deng <yang.jiajun1@xydigit.com>
Co-authored-by: 杨浩宇0668001029 <yang.haoyu@xydigit.com>
Co-authored-by: hailory <hailory@xydigit.com>
Co-authored-by: connermo <conner.mo@gmail.com>
Co-authored-by: Iliya Abolghasemi <gfaerny@gmail.com>
Co-authored-by: ben.li <li.yang6@xydigit.com>
Co-authored-by: harjoth <harjoth.khara@gmail.com>
2026-07-05 05:33:11 -07:00
Peter Steinberger
1f484a8dbd test: speed up and stabilize full suite 2026-07-05 08:00:23 -04:00
LZY3538
e6ec74c254 fix(compaction): preserve Anthropic context usage (#99864) 2026-07-05 02:33:43 -07:00
Masato Hoshino
153ee2abba fix(subagents): killed subagent runs stay running in the task list (#99806)
* fix(subagents): reconcile killed task outcomes

Co-authored-by: masatohoshino <g515hoshino@gmail.com>

* fix(subagents): retain reconciliation marker narrowing

* fix(subagents): honor generations in latest-run views

* test(subagents): align reconciliation fixtures

* fix(subagents): keep steer recovery best effort

* style(tasks): use type-only runtime contract import

* refactor(subagents): keep generation ordering leaf-only

* fix(subagents): sanitize persisted task owner ids

* fix(subagents): preserve failed replay lifecycle reason

* fix(agents): clear killed lifecycle timeout grace

* fix(tasks): preserve canonical subagent outcomes

* fix(agents): continue requester-wide cancellation

* fix(agents): fence yield revival races

* test(agents): mock detached task lookup

* fix: fence subagent cleanup deletion

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-05 02:17:10 -07:00
Peter Steinberger
a555ad7598 fix(reply): prevent foreground delivery deadlock (#100205)
* fix(reply): prevent foreground delivery deadlock

* fix(reply): prevent foreground delivery deadlock
2026-07-05 01:56:41 -07:00
Peter Steinberger
062f88e3e3 refactor: extract reusable AI runtime package (#99059)
* refactor: extract reusable AI runtime package

* refactor: complete AI provider relocation

* refactor: keep llm core internal

* refactor(ai): make @openclaw/ai self-contained with host policy ports

Move pure transport helpers (tool projections, strict-schema normalization,
prompt-cache boundary, stream guards, anthropic/openai compat, request
activity) from src into packages/ai; move utf16-slice into
normalization-core. Inject host policy (guarded fetch, redaction,
strict-tool defaults, diagnostics logging) through AiTransportHost with
inert library defaults installed by src/llm/stream.ts. Narrow the public
barrel to instance-scoped createApiRegistry/createLlmRuntime; the
process-default runtime moves behind internal/ and
registerBuiltInApiProviders takes an explicit registry. Delete the
src/llm/api-registry re-export facade.

* fix(ai): teach node, jiti, and vite resolvers the @openclaw/ai and utf16-slice subpaths

The workspace alias tables in root-alias.cjs, plugin-sdk-native-resolver,
sdk-alias, the shared vitest config, and the Control UI vite config only
knew @openclaw/llm-core; Node-side plugin loading resolved @openclaw/ai
through the pnpm symlink to the unbuilt dist (checks-node-compact CI
failures), and the Control UI build broke on the new
normalization-core/utf16-slice subpath.

* chore(ui): drop leftover service-worker debug logging

* build(release): ship @openclaw/ai with its own shrinkwrap and honest dependency set

packages/ai declares only its six real runtime deps (kysely, chalk, json5,
tslog, zod, fs-safe, and proxyline were never imported); orphaned root deps
removed. generate-npm-shrinkwrap now treats publishable packages/* like
publishable plugins so the AI tarball pins its transitive tree even though
workspace deps are omitted from the root shrinkwrap. knip learns the
package entry points; the tsdown dts neverBundle option moves to its
documented deps.dts home; the README documents the no-semver internal/*
contract and host ports.

* docs(ai): add minimal external-consumer example app

examples/ai-chat consumes only the public @openclaw/ai surface (built dist
via the workspace link): isolated runtime, built-in provider registration,
one streamed completion. Supports Anthropic/OpenAI via env keys and a
keyless local Ollama target; live-verified against Ollama.

* docs(ai): document the @openclaw/ai package and workspace shrinkwrap boundary

* chore(check): include examples/ in duplicate-scan targets

* fix: emit normalization package subpaths

* fix: complete AI package boundary artifacts

* fix: align AI package boundary contracts

* fix(ci): stabilize package release contracts

* test: align documentation contract checks

* test: keep cron docs guard aligned

* test: align restored docs contract guards

* test: follow upstream docs contracts

* docs: drop superseded talk wording
2026-07-05 01:56:40 -04:00
Vincent Koc
76d686cf61 fix(auto-reply): persist first command session updates 2026-07-05 07:09:53 +02:00
truiem-bot
420b3cdd29 feat(slack): support per-channel replyToMode (#82253)
* feat(slack): support per-channel replyToMode

* fix(slack): align per-channel threading contexts

* fix(slack): preserve queued channel reply mode

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-04 23:41:24 -04:00
Peter Steinberger
bafdec9868 fix: preserve codex transcript ownership (#100160)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-04 20:04:43 -07:00
Peter Steinberger
2ec765edea fix(auto-reply): deliver model directive acknowledgements (#100151) 2026-07-05 03:57:11 +01:00
Ayaan Zaidi
b34c188f8f fix(agents): surface Claude CLI thinking token progress 2026-07-04 19:43:11 -07:00
Ayaan Zaidi
32d791fee3 fix(auto-reply): bridge queued CLI reasoning streams 2026-07-04 19:43:11 -07:00
Peter Steinberger
1b8d837674 fix(tui): preserve queued prompt lifecycle (#100123)
Allow busy TUI sessions to forward prompts into the configured queue while
keeping queued-turn admission, cancellation, restart, and transcript ownership
consistent across the TUI, Gateway, and followup queue.

Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
2026-07-04 21:15:28 -04:00
Dallin Romney
194dd6fe50 refactor(security): consolidate secret primitives (#99746)
* refactor(security): consolidate secret primitives

* fix(plugin-sdk): align secret display metadata

* chore(plugin-sdk): refresh API baseline after rebase

* refactor(security): trim secret primitive consolidation
2026-07-04 16:38:13 -07:00
Marvinthebored
6445a0632d feat(cli): render claude CLI native thinking with /reasoning gating
The claude-cli (claude-stdio) live-session backend emits native
reasoning on stream-json (`thinking_delta` + a snapshot `thinking`
block), but the CLI parser had no thinking handling at all, so it was
silently dropped on every surface in every /reasoning mode.

- cli-output.ts: parse thinking_delta/snapshot into stream:"thinking"
  with replace-style per-content-block-index dedupe
  (assembleThinkingTextByIndex), robust to tool-interleaved multi-block
  turns (snapshot replaces per-index rather than assuming a streamed
  prefix). signature_delta/redacted_thinking stay skipped.
- agent-runner-cli-dispatch.ts: bridge stream:"thinking" into
  onReasoningStream via a new createReasoningTextBridge (mirrors the
  existing assistant-text bridge), replacing the old
  assistant-text-as-reasoning heuristic. Track the final reasoning
  snapshot and, when present, prepend a durable
  `{text, isReasoning: true}` payload before the answer — mirroring
  the embedded-runner durable reasoning payload
  (embedded-agent-runner/run/payloads.ts).
- agent-runner-execution.ts: thread isReasoningSnapshot through to
  onReasoningStream, still passing requiresReasoningProgressOptIn:true
  so CLI reasoning rides the exact same channel gates as embedded
  reasoning (Discord/Telegram): /reasoning off -> nothing on any
  surface, stream -> window preview only, on -> durable payload too.
  The bridge is suppressed under the same conditions as the assistant
  bridge (e.g. silentExpected follow-ups), so no durable payload is
  synthesized for turns that are expected to stay silent.

Supersedes this branch's prior commit (a straight port of
af7eb48e05's emit-always model, no gating and no durable mode) with
the above gated/durable design per product decision.

Also, in cli-output.ts: reset the per-content-block-index thinking
tracker on every new Anthropic message (message_start, or a fresh
top-level "assistant" snapshot's message.id) instead of leaving it
scoped to the whole CLI turn. Content-block indices restart at 0 per
Anthropic message, so a tool round-trip within one turn (message A:
thinking + tool_use; tool result fed back; message B: a fresh
message_start) was letting message A's stale index-0 thinking text
bleed into message B's index-0 delta.

And in agent-runner-cli-dispatch.ts: attach the durable reasoning
payload whenever any thinking was bridged, not only when the CLI also
produced a visible final answer, so a thinking-only turn (no visible
reply) doesn't silently drop its reasoning.

Round 2: the durable `{isReasoning: true}` payload synthesized above
was also reaching the QUEUED FOLLOW-UP delivery path
(followup-runner.ts -> resolveFollowupDeliveryPayloads) with no gate
at all, so a claude-cli queued follow-up with /reasoning off would
leak internal reasoning to the channel as an ordinary visible message.
resolveFollowupDeliveryPayloads (followup-delivery.ts) now takes a
reasoningPayloadsEnabled flag and drops isReasoning payloads unless
it is true; followup-runner.ts threads opts?.reasoningPayloadsEnabled
through at both call sites, from the same GetReplyOptions the direct
path already reads (dispatch-from-config.ts:2054). This makes the
queued-follow-up path apply the identical isReasoning/
reasoningPayloadsEnabled gate that direct dispatch already enforces
(dispatch-from-config.ts:3347, :3537) — no new mechanism, CLI
follow-ups now go through the exact same gate embedded follow-ups do.

Scope note: routeReply's own unconditional suppression of isReasoning
payloads on the origin-routing branch (route-reply.ts:131,
shouldSuppressReasoningPayload) is pre-existing, shared with the
embedded runner, and unchanged by this commit — that gate governs
whether a kept-and-routed reasoning payload is actually rendered on a
cross-channel queued follow-up, and is out of scope here. This commit
closes the leak (off -> never eligible on any path); it does not
change what happens to an eligible payload once it reaches routeReply.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-04 16:19:10 -07:00
xingzhou
27d6c88167 feat: add utility models and generated session titles (#87643)
* feat: add utility model session titles

* docs: clarify utility model calls

* fix: budget utility title reasoning

* docs: refresh config baseline

* docs: refresh plugin sdk baseline

* docs: refresh plugin sdk baseline

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-04 17:24:51 -04:00
Peter Steinberger
6df0fb818d feat: add session thread management (#98510)
* feat: add session thread management

Squash of codex/thread-management (025aefc3ad1) onto origin/main:
pin/archive/rename sessions via sessions.patch, archived-aware
sessions.list, lifecycle fencing, read-only archived chat, SDK +
Swift protocol support, Control UI session management.

* refactor(ui): minimal session rows with hover-revealed management

Chat picker and sidebar recents share session-row primitives: single-line
rows, relative timestamps, rename/archive/pin revealed on hover or focus,
accent pin badge for pinned rows, and an active-run spinner in the trail
slot. Sidebar floats pinned sessions above recency via the shared
comparator and gains archive/pin actions through the unified sessions-view
patch fallback. Archive eligibility is one shared policy
(canArchiveSessionRow); the sidebar/picker active-run tooltip now uses the
real sessionsView.activeRun locale key.

* fix: align session admission with mailbox-era main

Integration fixes after rebasing onto current main: sessions_list mailbox
test expectations learn the archived/pinned row fields and archived:false
list param; gateway agent admission treats a session as deleted only when
both the requested and canonical alias sets miss it (legacy bare-main
stores and exec-approval followups read under different spellings); cron
persist tests keep a consistent store across claim-guarded persist calls;
the ACP abort hook test asserts abort propagation instead of signal
identity; drop dead lifecycle writes flagged by no-useless-assignment and
fix the promise-executor return in the codex compact test.

* fix(qa): align UI e2e and shard fixtures with redesigned session rows

Sidebar session rows are wrapper divs with an inner link now: update the
navigation browser tests and chat-flow Playwright selectors. Seed a real
per-test session store for the auto-fallback admission guard instead of
depending on leftover host files at /tmp/sessions.json. Teach the
test-projects routing fixture about the suites that newly import the
shared temp-dir helper. Document the Codex thread-format contract for
archivedAt/pinnedAt (flag derived from server-stamped timestamp, epoch ms
here vs Codex epoch seconds) at the type and in the session docs.

* test: route auto-fallback suite through temp-dir helper plans

The auto-fallback suite now imports the shared temp-dir helper for its
seeded session store, so the top-level helper routing fixture must list
it in the auto-reply plan.
2026-07-04 14:30:47 -04:00
Vincent Koc
cf36cb915d test(auto-reply): update group intro prompt expectations 2026-07-04 18:31:55 +02:00
Peter Steinberger
49cc59b1e8 refactor: consolidate markdown code fences, error coercion, and byte-identical helper pairs (#99932)
* refactor(shared): add markdown code span/fence helpers and migrate seven call sites

* refactor(normalization-core): add canonical toErrorObject and migrate six copies

* refactor: consolidate byte-identical helper pairs onto owner modules

* refactor: reuse canonical path and token helpers in session tools and credentials

* refactor(packages): dedupe compaction summarization tail and session dir parsing

* fix(security): keep missing extensions dir silent in shared plugin dir lister

* refactor: reuse media-core chunk reader and shared dreaming session key helper

* fix(plugins): register error-coercion subpath in sdk alias table

* chore(plugin-sdk): re-pin callable export count after rebase

* chore(plugin-sdk): re-pin callable export count after rebase
2026-07-04 08:40:41 -04:00
Vincent Koc
a0e6048624 fix(agents): preserve mixed image attachment order (#99902) 2026-07-04 02:32:03 -07:00
Peter Steinberger
be95bb72d4 refactor: consolidate core stream cleanup, watchdog locality, and approval text duplication (#99901)
* refactor(infra): share guarded body-stream cleanup across fetch consumers

* refactor(agents): extract shared runtime model locality for llm watchdogs

* refactor(auto-reply): share exec approval route resolution between command handlers

* refactor(plugin-sdk): share approval reaction hint text helpers

* refactor(plugin-sdk): share planned migration target resolution

* refactor(infra): use canonical sleep helper in backup retry

* docs(plugins): mention shared migration targets and reaction hint helpers

* chore(plugin-sdk): pin surface budgets for reaction hint and migration target helpers
2026-07-04 02:08:35 -07:00
Peter Steinberger
3d404478b8 fix(discord): hide internal Code Mode wait progress (#99893)
Hide invocation-specific Code Mode wait controls from channel progress while preserving telemetry and visible non-internal tools.
2026-07-04 01:45:50 -07:00
Dallin Romney
5ea4b412a8 refactor: consolidate abort primitives (#99753) 2026-07-03 19:44:44 -07:00
Ivan Z
b381559184 fix(auto-reply): don't block reply completion on transcript mirror (#99549)
* fix(auto-reply): don't block reply completion on transcript mirror

mirrorTranscriptAfterDispatcherDelivery() opens with
`await dispatcher.waitForIdle()` and is awaited from sendFinalPayload,
i.e. before the reply operation completes. When a follow-up is queued
(queueDepth>=2) the dispatcher cannot go idle until this operation
clears, and the operation cannot clear until sendFinalPayload returns —
a completion<->dispatcher-idle deadlock. The reply operation stays
phase=running and surfaces as stalled_agent_run (recovery=none) until
the ~15-minute stuck-session abort.

Run the transcript mirror as fire-and-forget: it is post-delivery
bookkeeping and does not need to gate completion. The operation
completes immediately, the queued follow-up proceeds, the dispatcher
goes idle, and the detached mirror then records the transcript.

Validated live across three backends (LMStudio openai-completions,
claude-cli, DeepSeek cloud): queueDepth 2 and 3, zero stalls.

* test(auto-reply): regression coverage for detached transcript mirror

Two focused tests on the queueDepth>=2 completion<->dispatcher-idle deadlock:
- final reply completes even when dispatcher.waitForIdle() never resolves
  (pre-fix this awaited-mirror path deadlocked; the test hangs on the old code).
- a rejecting background mirror does not fail the reply (the detached .catch
  contains it instead of surfacing an unhandled rejection).

* fix(auto-reply): settle transcript mirrors after delivery

Co-authored-by: Zaytsev Ivan <ivan@jad.ru>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-03 18:19:58 -07:00
Dallin Romney
1a04c9c2ad refactor: consolidate string reader mechanics (#99676)
* refactor: consolidate string reader mechanics

* fix: declare model catalog normalization dependency

* fix: keep model catalog dependency-free

* refactor(core): keep string readers internal
2026-07-03 18:00:07 -07:00
Ayaan Zaidi
3706ebed33 fix(agents): keep mention state and group intro out of session-stable prompt hash 2026-07-03 17:58:36 -07:00
Dallin Romney
00b7eac5c3 refactor: consolidate number coercion callers (#99671)
* refactor: consolidate number coercion callers

* fix: stabilize plugin sdk surface resolution
2026-07-03 16:24:43 -07:00
SunnyShu
19d621e760 fix(auto-reply): strip stray punctuation before silent-reply token detection (#98224)
* [AI] fix(auto-reply): strip stray punctuation before silent-reply token detection

When the model emits .NO_REPLY or *NO_REPLY* instead of bare NO_REPLY,
isSilentReplyText() failed to detect it because the regex requires
the text to consist only of the token with optional whitespace.

Add stripEdgeNonAlnum() that strips leading/trailing non-letter,
non-number characters (Unicode-aware) before the regex test. This
catches stray punctuation while preserving the existing safeguard
against suppressing mixed-content text.

Related to #98166

Co-Authored-By: Claude <noreply@anthropic.com>

* [AI] fix: strip only punct not emoji before silent-reply detection

Change stripEdgeNonAlnum to stripEdgePunct: strip only punctuation (\p{P}) instead of all non-letter/non-number characters. Emoji would be stripped by the former, causing false silent-reply on mixed content.

Related to #98166

Co-Authored-By: Claude <noreply@anthropic.com>

* [AI] fix: preserve exact custom-token match before edge-punct normalization

Try exact regex match first, then fall back to stripEdgePunct.
This preserves exact matching for custom tokens whose first/last
character is punctuation (e.g. *SILENT*, #QUIET#), while still
catching the common case of NO_REPLY wrapped in stray punctuation.

Add regression test for punctuation-edged custom tokens.

ClawSweeper: https://github.com/openclaw/openclaw/pull/98224#issuecomment-4846415801

Related to #98166

Co-Authored-By: Claude <noreply@anthropic.com>

* [AI] fix: handle whitespace-wrapped punctuation in silent-reply fallback

Trim whitespace before stripping edge punctuation in the fallback
path so variants like " .NO_REPLY " and "NO_REPLY. " are also
caught by the silent-reply detector.

Add regression coverage for whitespace-wrapped edge punctuation.

ClawSweeper: https://github.com/openclaw/openclaw/pull/98224#issuecomment-4846415801

Related to #98166

Co-Authored-By: Claude <noreply@anthropic.com>

* test(auto-reply): tighten silent-token boundaries

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-03 14:22:51 -07:00
Ayaan Zaidi
48e8965b10 fix(agents): keep cli session binding facts session-stable
Keep CLI session reuse hashes keyed to stable session prompt facts while preserving per-turn room-event message-tool delivery for execution and MCP scoping.

Thread cliSessionBindingFacts from reply assembly through queued/followup CLI runs, make the stable CLI prompt delivery-neutral, and cover production-shaped room-event opts so forced message_tool_only delivery cannot poison binding identity.

Fixes #99372

Release note: Telegram/room-event CLI backends can resume sessions across interactive turns again instead of replaying full history after system-prompt invalidations.
2026-07-03 09:01:26 -07:00
LZY3538
e272da5c55 fix: keep always-on group fallback messages in dispatch (#99506)
* fix(auto-reply): honor always-on plugin fallback groups

* fix(channels): preserve mention policy across bindings

* test(discord): type mention policy dispatch context

* test(discord): type mention policy dispatch context

* fix(whatsapp): preserve effective mention policy

* fix(whatsapp): preserve effective mention policy

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Co-authored-by: Peter Steinberger <peter@steipete.me>
2026-07-03 07:26:45 -07:00
Eva
414ecd2b96 Preserve Codex output after missing turn completion (#99217)
* Preserve Codex output after missing turn completion

* fix: narrow Codex completion-timeout output recovery

* test(codex): narrow binding path guard

* fix: narrow Codex completion-timeout output recovery

* test(codex): narrow binding path guard

* fix(codex): preserve id-less post-tool replies

---------

Co-authored-by: Sedrak-Hovhannisyan <264150421+Sedrak-Hovhannisyan@users.noreply.github.com>
Co-authored-by: Eva <eva@100yen.org>
Co-authored-by: Jason (Json) <263060202+fuller-stack-dev@users.noreply.github.com>
2026-07-03 05:07:54 -07:00
Jesse Merhi
8abc633cf8 Add Signal status reaction lifecycle (#98791) 2026-07-03 21:42:39 +10:00
Jesse Merhi
776b34a4e6 fix: chat.send no reply when thinking metadata is set (#98855)
* fix(gateway): preserve chat thinking metadata

* test: harden chat thinking metadata coverage

* test: harden chat thinking metadata coverage
2026-07-03 21:03:41 +10:00
SunnyShu
68e06b9ea1 fix(gateway): include session label in deriveSessionTitle fallback chain (#98841)
* fix(gateway): include session label in deriveSessionTitle fallback chain

When a session is renamed with /name, the user-provided label is persisted
in sessions.json but was ignored by deriveSessionTitle. This caused the
TUI session picker and other derived-title consumers to show auto-generated
or UUID-based names instead of the user's label.

Add entry.label to the deriveSessionTitle fallback chain after
displayName and subject, before the auto-derived firstUserMessage and
sessionId fallbacks. This aligns with the existing ACP translator
precedence (derivedTitle ?? displayName ?? label ?? key).

Related to #98742

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* test(gateway): tighten session label precedence coverage

* fix(gateway): prioritize explicit session labels

* fix(commands): preserve named-session suggestions

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-03 02:09:31 -07:00
Ayaan Zaidi
4e6933c84f fix(auto-reply): restore per-turn message-tool delivery contract
Addressed source-reply turns now carry the message-tool-only delivery hint in runtime prompt context, matching the room-event per-turn contract without persisting the hint into transcript rows.

Surface: auto-reply prompt envelopes and agent messaging guidance. Fixes #99371.

Release-note: fixes Telegram group replies under message_tool_only delivery being composed privately instead of being prompted to use message(action=send).
2026-07-02 23:11:32 -07:00
Ayaan Zaidi
0bf66ab7bd fix(sessions): scope ambient transcript watermark to session id
Ambient transcript watermarks now carry the transcript session id, resolve only for the current session entry, and skip stale room-event hooks that no longer match the prepared transcript session.

This protects Telegram group prompt windows after reset by backfilling rows that are no longer present in the new session transcript, while preserving steady-state watermark filtering within one session.

Fixes #99373

Release-note: fixes Telegram group context loss after session reset when ambient transcript watermarks outlived the transcript they referenced.
2026-07-02 22:57:01 -07:00
Dallin Romney
4f4e9a4c78 refactor(shared): consolidate core record guards (#99359) 2026-07-02 22:13:17 -07:00