Commit Graph

1085 Commits

Author SHA1 Message Date
Marcus Castro
07850e8a93 fix(media): strip MEDIA: prefix in loadWebMediaInternal (#13107)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 9d95e6af5a
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-14 21:41:26 +01:00
Peter Steinberger
05e2957edc refactor(test): dedupe block streaming runner setup 2026-02-14 20:23:33 +00:00
Peter Steinberger
5daaab3692 perf(test): slim raw-body directive integration 2026-02-14 20:12:27 +00:00
Peter Steinberger
e1220c48f5 perf(test): skip skills snapshot work in fast env 2026-02-14 20:12:27 +00:00
Peter Steinberger
9762e48134 perf(test): speed up block streaming tests 2026-02-14 20:12:27 +00:00
Peter Steinberger
cfc2604d3e perf(test): speed up heartbeat typing suite 2026-02-14 20:12:27 +00:00
Peter Steinberger
387fb40745 perf(test): skip heavy boot paths in reply suites 2026-02-14 20:12:26 +00:00
Peter Steinberger
2b9a501b77 refactor(test): dedupe directive behavior e2e setup 2026-02-14 19:55:10 +00:00
Peter Steinberger
5b7a33272a test: stabilize vitest mocks and harness typing 2026-02-14 20:45:05 +01:00
Peter Steinberger
eb594a090d refactor(test): dedupe trigger-handling e2e setup 2026-02-14 19:04:39 +00:00
Peter Steinberger
0e824a178a refactor(test): share runReplyAgent typing heartbeat harness 2026-02-14 19:04:39 +00:00
Peter Steinberger
4d8a4fbb48 refactor(test): share runReplyAgent memory flush harness 2026-02-14 19:04:39 +00:00
Peter Steinberger
7582e93a8e perf(test): speed up raw-body reply test 2026-02-14 17:56:39 +00:00
Peter Steinberger
fe2d883cf7 perf(test): remove fs skill scanning from skill-commands tests 2026-02-14 16:36:15 +00:00
Peter Steinberger
5349a0f7c2 perf(test): mock reserved commands in skill-commands tests 2026-02-14 16:36:15 +00:00
Peter Steinberger
8ff2787981 perf(test): speed up skill-commands tests 2026-02-14 16:36:15 +00:00
Peter Steinberger
e401e2584d refactor(auto-reply): share elevated unavailable message 2026-02-14 15:39:45 +00:00
Peter Steinberger
4b1cadaecb refactor(media): normalize inbound media type defaults (#16228) 2026-02-14 15:06:13 +01:00
Peter Steinberger
ef70a55b7a refactor(reply): clarify explicit reply tags in off mode (#16189)
* refactor(reply): clarify explicit reply tags in off mode

* fix(plugin-sdk): alias account-id subpath for extensions
2026-02-14 14:15:37 +01:00
Aldo
7b39543e8d fix(reply): honour explicit [[reply_to_*]] tags when replyToMode is off (#16174)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 778fc2559a
Co-authored-by: aldoeliacim <17973757+aldoeliacim@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-14 13:29:42 +01:00
Peter Steinberger
eb4215d570 perf(test): speed up Vitest bootstrap 2026-02-14 12:13:27 +00:00
Pejman Pour-Moezzi
9475791d98 fix: update remaining replyToMode "first" defaults to "off"
- src/channels/dock.ts: core channel dock fallback
- src/auto-reply/reply/reply-routing.test.ts: test expectation
- docs/zh-CN/channels/telegram.md: Chinese docs reference

Comprehensive grep confirms no remaining Telegram-specific "first"
defaults after this commit.
2026-02-13 23:31:17 -08:00
Peter Steinberger
b4430c126a perf(test): trim duplicate raw-body and streaming queue scenarios 2026-02-14 01:49:54 +00:00
Peter Steinberger
38098442ca perf(test): reduce setup churn in block streaming and docker tests 2026-02-14 01:26:12 +00:00
Peter Steinberger
aa6d8b27ac perf(test): merge queue integration coverage and shrink media fixture 2026-02-14 00:50:14 +00:00
Artale
67b5c093b5 fix(auto-reply): allow image-only messages to reach the agent (openclaw#12352) thanks @arosstale
Verified:
- pnpm build
- pnpm check
- pnpm test

Co-authored-by: arosstale <117890364+arosstale@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-13 18:42:22 -06:00
Spacefish
f9379ecee2 Ignore up to 4 non-word characters when stripping HEARTBEAT_OK token … (#15847)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: dc03ce5005
Co-authored-by: Spacefish <375633+Spacefish@users.noreply.github.com>
Co-authored-by: steipete <58493+steipete@users.noreply.github.com>
Reviewed-by: @steipete
2026-02-14 01:36:04 +01:00
Peter Steinberger
fecb3f326e perf(test): trim models/browser suite overhead 2026-02-14 00:08:02 +00:00
Peter Steinberger
23e8f3a20a perf(test): merge block-streaming scenarios into single fixture run 2026-02-13 23:54:28 +00:00
Peter Steinberger
4bef423d83 perf(test): reduce gateway reload waits and trim duplicate invoke coverage 2026-02-13 23:50:08 +00:00
solstead
ab71fdf821 Plugin API: compaction/reset hooks, bootstrap file globs, memory plugin status (#13287)
* feat: add before_compaction and before_reset plugin hooks with session context

- Pass session messages to before_compaction hook
- Add before_reset plugin hook for /new and /reset commands
- Add sessionId to plugin hook agent context

* feat: extraBootstrapFiles config with glob pattern support

Add extraBootstrapFiles to agent defaults config, allowing glob patterns
(e.g. "projects/*/TOOLS.md") to auto-load project-level bootstrap files
into agent context every turn. Missing files silently skipped.

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

* fix(status): show custom memory plugins as enabled, not unavailable

The status command probes memory availability using the built-in
memory-core manager. Custom memory plugins (e.g. via plugin slot)
can't be probed this way, so they incorrectly showed "unavailable".
Now they show "enabled (plugin X)" without the misleading label.

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

* fix: use async fs.glob and capture pre-compaction messages

- Replace globSync (node:fs) with fs.glob (node:fs/promises) to match
  codebase conventions for async file operations
- Capture session.messages BEFORE replaceMessages(limited) so
  before_compaction hook receives the full conversation history,
  not the already-truncated list

* fix: resolve lint errors from CI (oxlint strict mode)

- Add void to fire-and-forget IIFE (no-floating-promises)
- Use String() for unknown catch params in template literals
- Add curly braces to single-statement if (curly rule)

* fix: resolve remaining CI lint errors in workspace.ts

- Remove `| string` from WorkspaceBootstrapFileName union (made all
  typeof members redundant per no-redundant-type-constituents)
- Use type assertion for extra bootstrap file names
- Drop redundant await on fs.glob() AsyncIterable (await-thenable)

* fix: address Greptile review — path traversal guard + fs/promises import

- workspace.ts: use path.resolve() + traversal check in loadExtraBootstrapFiles()
- commands-core.ts: import fs from node:fs/promises, drop fs.promises prefix

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

* fix: resolve symlinks before workspace boundary check

Greptile correctly identified that symlinks inside the workspace could
point to files outside it, bypassing the path prefix check. Now uses
fs.realpath() to resolve symlinks before verifying the real path stays
within the workspace boundary.

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

* fix: address Greptile review — hook reliability and type safety

1. before_compaction: add compactingCount field so plugins know both
   the full pre-compaction message count and the truncated count being
   fed to the compaction LLM. Clarify semantics in comment.

2. loadExtraBootstrapFiles: use path.basename() for the name field
   so "projects/quaid/TOOLS.md" maps to the known "TOOLS.md" type
   instead of an invalid WorkspaceBootstrapFileName cast.

3. before_reset: fire the hook even when no session file exists.
   Previously, short sessions without a persisted file would silently
   skip the hook. Now fires with empty messages array so plugins
   always know a reset occurred.

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

* fix: validate bootstrap filenames and add compaction hook timeout

- Only load extra bootstrap files whose basename matches a recognized
  workspace filename (AGENTS.md, TOOLS.md, etc.), preventing arbitrary
  files from being injected into agent context.
- Wrap before_compaction hook in a 30-second Promise.race timeout so
  misbehaving plugins cannot stall the compaction pipeline.
- Clarify hook comments: before_compaction is intentionally awaited
  (plugins need messages before they're discarded) but bounded.

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

* fix: make before_compaction non-blocking, add sessionFile to after_compaction

- before_compaction is now true fire-and-forget — no await, no timeout.
  Plugins that need full conversation data should persist it themselves
  and return quickly, or use after_compaction for async processing.
- after_compaction now includes sessionFile path so plugins can read
  the full JSONL transcript asynchronously. All pre-compaction messages
  are preserved on disk, eliminating the need to block compaction.
- Removes Promise.race timeout pattern that didn't actually cancel
  slow hooks (just raced past them while they continued running).

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

* feat: add sessionFile to before_compaction for parallel processing

The session JSONL already has all messages on disk before compaction
starts. By providing sessionFile in before_compaction, plugins can
read and extract data in parallel with the compaction LLM call rather
than waiting for after_compaction. This is the optimal path for memory
plugins that need the full conversation history.

sessionFile is also kept on after_compaction for plugins that only
need to act after compaction completes (analytics, cleanup, etc.).

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

* refactor: move bootstrap extras into bundled hook

---------

Co-authored-by: Solomon Steadman <solstead@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Clawdbot <clawdbot@alfie.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 00:45:45 +01:00
Peter Steinberger
d5e25e0ad8 refactor: centralize dispatcher lifecycle ownership 2026-02-14 00:41:37 +01:00
Peter Steinberger
5caf829d28 perf(test): trim duplicate gateway and auto-reply test overhead 2026-02-13 23:40:38 +00:00
Peter Steinberger
ad57e561c6 refactor: unify gateway restart deferral and dispatcher cleanup 2026-02-14 00:38:18 +01:00
Peter Steinberger
e794ef0478 perf(test): reduce hot-suite setup and duplicate test work 2026-02-13 23:30:41 +00:00
Bridgerz
ab4a08a82a fix: defer gateway restart until all replies are sent (#12970)
* fix: defer gateway restart until all replies are sent

Fixes a race condition where gateway config changes (e.g., enabling
plugins via iMessage) trigger an immediate SIGUSR1 restart, killing the
iMessage RPC connection before replies are delivered.

Both restart paths (config watcher and RPC-triggered) now defer until
all queued operations, pending replies, and embedded agent runs complete
(polling every 500ms, 30s timeout). A shared emitGatewayRestart() guard
prevents double SIGUSR1 when both paths fire simultaneously.

Key changes:
- Dispatcher registry tracks active reply dispatchers globally
- markComplete() called in finally block for guaranteed cleanup
- Pre-restart deferral hook registered at gateway startup
- Centralized extractDeliveryInfo() for session key parsing
- Post-restart sentinel messages delivered directly (not via agent)
- config-patch distinguished from config-apply in sentinel kind

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

* fix: single-source gateway restart authorization

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 00:29:29 +01:00
Peter Steinberger
e324cb5b94 perf(test): reduce fixture churn in hot suites 2026-02-13 23:16:41 +00:00
Peter Steinberger
dac8f5ba3f perf(test): trim fixture and import overhead in hot suites 2026-02-13 23:16:41 +00:00
Marcus Castro
31537c669a fix: archive old transcript files on /new and /reset (#14949)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 4724df7dea
Co-authored-by: mcaxtr <7562095+mcaxtr@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-13 14:55:16 -05:00
Peter Steinberger
fdfc34fa1f perf(test): stabilize e2e harness and reduce flaky gateway coverage 2026-02-13 17:32:14 +00:00
Marcus Castro
d91e995e46 fix(inbound): preserve literal backslash-n sequences in Windows paths (#11547)
* fix(inbound): preserve literal backslash-n sequences in Windows paths

The normalizeInboundTextNewlines function was converting literal backslash-n
sequences (\n) to actual newlines, corrupting Windows paths like
C:\Work\nxxx\README.md when sent through WebUI.

This fix removes the .replaceAll("\\n", "\n") operation, preserving
literal backslash-n sequences while still normalizing actual CRLF/CR to LF.

Fixes #7968

* fix(test): set RawBody to Windows path so BodyForAgent fallback chain tests correctly

* fix: tighten Windows path newline regression coverage (#11547) (thanks @mcaxtr)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-13 18:24:01 +01:00
Peter Steinberger
4169a4df79 perf(test): remove redundant status module reloads 2026-02-13 15:11:38 +00:00
Peter Steinberger
990413534a fix: land multi-agent session path fix + regressions (#15103) (#15448)
Co-authored-by: Josh Lehman <josh@martian.engineering>
2026-02-13 14:17:24 +01:00
Lucky
e3cb2564d7 Agents: allow gpt-5.3-codex-spark in fallback and thinking (#14990)
* Agents: allow gpt-5.3-codex-spark in fallback and thinking

* Fix: model picker issue for openai-codex/gpt-5.3-codex-spark

Fixed an issue in the model picker.
2026-02-13 11:39:22 +00:00
Peter Steinberger
417509c539 test: stabilize local-timestamp assertion in session resets 2026-02-13 04:58:11 +00:00
青雲
fd076eb43a fix: /status shows incorrect context percentage — totalTokens clamped to contextTokens (#15114) (#15133)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: a489669fc7
Co-authored-by: echoVic <16428813+echoVic@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-12 23:52:19 -05:00
Masataka Shinohara
b93ad2cd48 fix(slack): populate thread session with existing thread history (#7610)
* feat(slack): populate thread session with existing thread history

When a new session is created for a Slack thread, fetch and inject
the full thread history as context. This preserves conversation
continuity so the bot knows what it previously said in the thread.

- Add resolveSlackThreadHistory() to fetch all thread messages
- Add ThreadHistoryBody to context payload
- Use thread history instead of just thread starter for new sessions

Fixes #4470

* chore: remove redundant comments

* fix: use threadContextNote in queue body

* fix(slack): address Greptile review feedback

- P0: Use thread session key (not base session key) for new-session check
  This ensures thread history is injected when the thread session is new,
  even if the base channel session already exists.

- P1: Fetch up to 200 messages and take the most recent N
  Slack API returns messages in chronological order (oldest first).
  Previously we took the first N, now we take the last N for relevant context.

- P1: Batch resolve user names with Promise.all
  Avoid N sequential API calls when resolving user names in thread history.

- P2: Include file-only messages in thread history
  Messages with attachments but no text are now included with a placeholder
  like '[attached: image.png, document.pdf]'.

- P2: Add documentation about intentional 200-message fetch limit
  Clarifies that we intentionally don't paginate; 200 covers most threads.

* style: add braces for curly lint rule

* feat(slack): add thread.initialHistoryLimit config option

Allow users to configure the maximum number of thread messages to fetch
when starting a new thread session. Defaults to 20. Set to 0 to disable
thread history fetching entirely.

This addresses the optional configuration request from #2608.

* chore: trigger CI

* fix(slack): ensure isNewSession=true on first thread turn

recordInboundSession() in prepare.ts creates the thread session entry
before session.ts reads the store, causing isNewSession to be false
on the very first user message in a thread. This prevented thread
context (history/starter) from being injected.

Add IsFirstThreadTurn flag to message context, set when
readSessionUpdatedAt() returns undefined for the thread session key.
session.ts uses this flag to force isNewSession=true.

* style: format prepare.ts for oxfmt

* fix: suppress InboundHistory/ThreadStarterBody when ThreadHistoryBody present (#13912)

When ThreadHistoryBody is fetched from the Slack API (conversations.replies),
it already contains pending messages and the thread starter. Passing both
InboundHistory and ThreadStarterBody alongside ThreadHistoryBody caused
duplicate content in the LLM context on new thread sessions.

Suppress InboundHistory and ThreadStarterBody when ThreadHistoryBody is
present, since it is a strict superset of both.

* remove verbose comment

* fix(slack): paginate thread history context fetch

* fix(slack): wire session file path options after main merge

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-13 05:51:04 +01:00
Gustavo Madeira Santana
ac41176532 Auto-reply: fix non-default agent session transcript path resolution (#15154)
* Auto-reply: fix non-default agent transcript path resolution

* Auto-reply: harden non-default agent transcript lookups

* Auto-reply: harden session path resolution across agent stores
2026-02-12 23:23:12 -05:00
Peter Steinberger
79a38858ae fix: preserve off-mode semantics in auto reply threading (#14976) (thanks @Diaspar4u) 2026-02-13 05:22:14 +01:00
Andrey
3d89f0f14a fix(reply): auto-inject replyToCurrent for reply threading
replyToMode "first"/"all" only filters replyToId but never generates
it — that required the LLM to emit [[reply_to_current]] tags. Inject
replyToCurrent:true on all payloads so applyReplyTagsToPayload sets
replyToId=currentMessageId, then let the existing mode filter decide
which replies keep threading (first only, all, or off).

Covers both final reply path (reply-payloads.ts) and block streaming
path (agent-runner-execution.ts).
2026-02-13 05:22:14 +01:00