Commit Graph

82 Commits

Author SHA1 Message Date
Josh Avant
b1f8172867 fix(secretrefs): resolve external channel contracts (#76449) 2026-05-02 23:48:11 -05:00
Peter Steinberger
0141471dd5 refactor: move shared helpers off reserved sdk seams 2026-04-27 13:07:54 +01:00
Peter Steinberger
2e2a134489 perf: lazy load bluebubbles catchup 2026-04-24 12:08:34 +01:00
Omar Shahine
77d9fd693f fix(bluebubbles): restore inbound image attachments and accept updated-message events (#67510)
* fix(bluebubbles): restore inbound image attachments and accept updated-message events

Four interconnected fixes for BlueBubbles inbound media:

1. Strip bundled-undici dispatcher from non-SSRF fetch path so attachment
   downloads no longer silently fail on Node 22+ (#64105, #61861)

2. Accept updated-message webhook events that carry attachments instead of
   filtering them as non-reaction events (#65430)

3. Include eventType in the persistent GUID dedup key so updated-message
   follow-ups are not rejected as duplicates of the original new-message (#52277)

4. Retry attachment fetch from BB API (2s delay) when the initial webhook
   arrives with an empty attachments array — image-only messages and
   updated-message events only (#67437)

Closes #64105, closes #61861, closes #65430.

* fix(bluebubbles): resolve review findings — SSRF policy, reuse extractAttachments, add tests

- F1 (BLOCKER): pass undefined instead of {} for SSRF policy when
  allowPrivateNetwork is false, so localhost BB servers are not blocked.
- F2 (IMPORTANT): reuse exported extractAttachments() from monitor-normalize
  instead of duplicating field extraction logic.
- F3 (IMPORTANT): simplify asRecord(asRecord(payload)?.data) to
  asRecord(payload.data) since payload is already Record<string, unknown>.
- F4 (NIT): bind retryMessageId before the guard to eliminate non-null assertion.
- F5 (IMPORTANT): add 4 tests for fetchBlueBubblesMessageAttachments covering
  success, non-ok HTTP, empty data, and guid-less entries.
- Add CHANGELOG entry for the user-facing fix.

* fix(ci): update raw-fetch allowlist line number after dispatcher strip

* fix(bluebubbles): resolve PR review findings (#67510)

- monitor-processing: move attachment retry into the !rawBody guard so
  image-only new-message events that arrive with empty attachments and
  empty text are recovered via a BB API refetch before being dropped.
  The existing retry block at the end of processMessageAfterDedupe was
  unreachable for this case because the !rawBody early-return fired
  first. (Greptile)
- monitor: derive isAttachmentUpdate from the normalized message shape
  instead of raw payload.data.attachments so updated-message webhooks
  with attachments under wrapper formats (payload.message, JSON-string
  payloads) are correctly routed through for processing instead of
  silently filtered. (Codex)
- types: use bundled-undici fetch when init.dispatcher is present so
  the SSRF guard's DNS-pinning dispatcher is preserved when this
  function is called as fetchImpl from guarded callers (e.g. the
  attachment download path via fetchRemoteMedia). Falls back to
  globalThis.fetch when no dispatcher is present so tests that stub
  globalThis.fetch keep working. (Codex)
- attachments: blueBubblesPolicy returns undefined for the non-private
  case (matching monitor-processing's helper) so sendBlueBubblesAttachment
  stops routing localhost BB through the SSRF guard. (Greptile)
- scripts/check-no-raw-channel-fetch: bump the types.ts allowlist line
  to match the restructured non-SSRF branch.

* fix(bluebubbles): move attachment retry before rawBody guard, fix stale log

Move the attachment retry block (2s BB API refetch for empty attachments)
before the !rawBody early-return guard. Previously, image-only messages
with text='' and attachments=[] would be dropped by the !rawBody check
before the retry could fire, making fix #4 dead code for its primary
use-case. Now the retry runs first and recomputes the placeholder from
resolved attachments so rawBody becomes non-empty when media is found.

Also fix stale log message that still said 'without reaction' after the
filter was expanded to pass through attachment updates.

* fix(bluebubbles): revert undici import, restore dispatcher-strip approach

Revert the @claude bot's undici import in types.ts — it introduced a
direct 'undici' dependency that is not declared in the BB extension's
package.json and would break isolated plugin installs. Restore the
original dispatcher-strip approach which is correct: the SSRF guard
already completed validation upstream before calling this function as
fetchImpl, so stripping the dispatcher does not weaken security.

* fix(bluebubbles): remove dead empty-body recovery block in !rawBody guard

The empty-body attachment-recovery block added in the earlier PR revision
is now redundant because the main retry block was moved above the rawBody
computation in 0d7d1c4208. Worse, that leftover block reassigned the
(now-const) placeholder variable, throwing `TypeError: Assignment to
constant variable` at runtime for image-only messages — breaking the very
recovery path it was meant to protect (flagged by Codex on 4bfc2777).

Remove the dead block; the up-front retry already handles the image-only
case by recovering attachments before the rawBody computation, so once we
reach the !rawBody guard with an empty body it is genuinely empty and
should drop as before.

* fix(ci): update raw-fetch allowlist line after dispatcher-strip revert

279dba17d2 reverted types.ts back to the dispatcher-strip approach,
which put the `fetch(url, ...)` call at line 189 instead of line 198.
Bump the allowlist entry to match so `lint:tmp:no-raw-channel-fetch`
stops failing check-additional.

* test(pdf-tool): update stale opus-4-6 constant to opus-4-7

`628b454eff feat: default Anthropic to Opus 4.7` bumped the bundled
anthropic image default to `claude-opus-4-7` but missed updating the
`ANTHROPIC_PDF_MODEL` constant in pdf-tool.model-config.test.ts. The
tests now fail on any PR that runs the `checks-node-agentic-agents-plugins`
shard because the resolver returns 4-7 while the test asserts 4-6.

Bump the constant to 4-7 to match the bundled default.

---------

Co-authored-by: Lobster <10343873+omarshahine@users.noreply.github.com>
2026-04-16 10:04:20 -07:00
Omar Shahine
6f1d321aab feat(bluebubbles): replay missed webhook messages after gateway restart (#66857)
Adds an in-process startup catchup pass to the BlueBubbles channel that
queries BB Server for messages delivered since a persisted per-account
cursor and re-feeds each through the existing processMessage pipeline.

Fixes the missed-message hole documented in #66721: BB's WebhookService
is fire-and-forget on POST failure, and MessagePoller only re-fires
webhooks on BB-side reconnection events, not on webhook-receiver
recovery.

- New extensions/bluebubbles/src/catchup.ts with singleflight per
  accountId, cursor persistence via the canonical state-paths
  resolver, bounded query (perRunLimit + maxAgeMinutes), failure-held
  cursor, truncation-aware page-boundary advancement, future-cursor
  recovery, isFromMe filter (pre- and post-normalization).
- monitor.ts fires catchup as a background task after the webhook
  target registers.
- config-schema.ts adds optional catchup block; accounts.ts adds
  catchup to nestedObjectKeys for deep-merge per-account overrides.
- Dedupes against #66816's persistent inbound GUID cache.
- 22 scoped tests; full BB suite 411/411; pnpm check green; live E2E
  on macOS 26.3 / BB Server 1.9.x recovered 3/3 missed messages.

Closes #66721.

Co-authored-by: Omar Shahine <omar@shahine.com>
2026-04-14 19:20:42 -07:00
James Reagan
dac72889e5 fix(bluebubbles): localhost probe respects private-network opt-out (#59373)
* honor localhost private-network policy

* drop flaky monitor private-network test

* align mocks and imports

* preserve account private-network overrides

* keep default account config

* strip stale private-network aliases

* fix(bluebubbles): remove unused channel imports

* fix: add changelog for bluebubbles private-network opt-out landing (#59373) (thanks @jpreagan)

---------

Co-authored-by: Shadow <hi@shadowing.dev>
2026-04-07 11:29:21 -05:00
Peter Steinberger
bbcc95948e refactor: dedupe provider lowercase helpers 2026-04-07 15:53:50 +01:00
Peter Steinberger
9e2a1e12fd refactor: dedupe channel runtime error formatting 2026-04-07 02:03:34 +01:00
Peter Steinberger
ca73e598e0 refactor: dedupe bluebubbles monitor record helper 2026-04-06 22:54:47 +01:00
Peter Steinberger
8cb85ff85f refactor: harden plugin metadata and bundled channel entry seams 2026-04-06 00:15:38 +01:00
Vincent Koc
c863ee1b86 fix(config): migrate bundled private-network aliases (#60862)
* refactor(plugin-sdk): centralize private-network opt-in semantics

* fix(config): migrate bundled private-network aliases

* fix(config): add bundled private-network doctor adapters

* fix(config): expose bundled channel migration hooks

* fix(config): prefer canonical private-network key

* test(config): refresh rebased private-network outputs
2026-04-05 08:49:44 +01:00
Vincent Koc
688eb8435b test(bluebubbles): split webhook ingress seam 2026-04-03 20:58:03 +09:00
Agustin Rivera
be10ecef77 fix(compare): reuse shared secret comparison helper (#58432)
* fix(compare): reuse shared secret comparison helper

* fix(compare): reject empty bluebubbles auth tokens

* docs: add changelog entry for shared secret comparison fix

---------

Co-authored-by: Jacob Tomlinson <jtomlinson@nvidia.com>
2026-04-02 13:53:19 +01:00
Jacob Tomlinson
5e08ce36d5 fix(bluebubbles): throttle webhook auth guesses (#55133)
* fix(bluebubbles): throttle webhook auth guesses

* test(bluebubbles): isolate attachment ssrf config

* test(bluebubbles): hoist attachment mocks

* docs: refresh bluebubbles config baseline

* fix(bluebubbles): trust proxied webhook client IPs

* fix(bluebubbles): honor trusted proxy webhook IPs

* fix(bluebubbles): honor real-ip fallback for webhooks
2026-03-26 14:54:03 +00:00
Jacob Tomlinson
f92c92515b fix(extensions): route fetch calls through fetchWithSsrFGuard (#53929)
* fix(extensions): route fetch calls through fetchWithSsrFGuard

Replace raw fetch() with fetchWithSsrFGuard in BlueBubbles, Mattermost,
Nextcloud Talk, and Thread Ownership extensions so outbound requests go
through the shared DNS-pinning and network-policy layer.

BlueBubbles: thread allowPrivateNetwork from account config through all
fetch call sites (send, chat, reactions, history, probe, attachments,
multipart). Add _setFetchGuardForTesting hook for test overrides.

Mattermost: add guardedFetchImpl wrapper in createMattermostClient that
buffers the response body before releasing the dispatcher. Handle
null-body status codes (204/304).

Nextcloud Talk: wrap both sendMessage and sendReaction with
fetchWithSsrFGuard and try/finally release.

Thread Ownership: add fetchWithSsrFGuard and ssrfPolicyFromAllowPrivateNetwork
to the plugin SDK surface; use allowPrivateNetwork:true for the
Docker-internal forwarder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(extensions): improve null-body handling and test harness cleanup

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bluebubbles): default to strict SSRF policy when allowPrivateNetwork is unset

Callers that omit allowPrivateNetwork previously got undefined policy,
which caused blueBubblesFetchWithTimeout to fall through to raw fetch
and bypass the SSRF guard entirely.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(bluebubbles): thread allowPrivateNetwork through action and monitor call sites

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(mattermost,nextcloud-talk): add allowPrivateNetwork config for self-hosted/LAN deployments

* fix: regenerate config docs baseline for new allowPrivateNetwork fields

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 02:04:54 -07:00
Vincent Koc
2f21eeb3cb Plugins: internalize bluebubbles SDK imports 2026-03-17 20:21:00 -07:00
Peter Steinberger
4b61779a46 refactor: unify extension webhook request lifecycle scaffolding 2026-03-07 23:27:51 +00:00
Gustavo Madeira Santana
9cfec9c05e Plugins/bluebubbles: migrate to scoped plugin-sdk imports 2026-03-04 02:35:12 -05:00
Gustavo Madeira Santana
ff38bc7649 Extensions: migrate bluebubbles plugin-sdk imports 2026-03-04 01:20:48 -05:00
Peter Steinberger
1c9deeda97 refactor: split webhook ingress and policy guards 2026-03-02 18:02:21 +00:00
Peter Steinberger
d3e8b17aa6 fix: harden webhook auth-before-body handling 2026-03-02 17:21:09 +00:00
Peter Steinberger
7a7eee920a refactor(gateway): harden plugin http route contracts 2026-03-02 16:48:00 +00:00
Peter Steinberger
2fd8264ab0 refactor(gateway): hard-break plugin wildcard http handlers 2026-03-02 16:24:06 +00:00
Peter Steinberger
b13d48987c refactor(gateway): unify control-ui and plugin webhook routing 2026-03-02 16:18:12 +00:00
Peter Steinberger
283029bdea refactor(security): unify webhook auth matching paths 2026-02-21 11:52:34 +01:00
Peter Steinberger
6b2f2811dc fix(security): require BlueBubbles webhook auth 2026-02-21 11:41:50 +01:00
Peter Steinberger
b8b43175c5 style: align formatting with oxfmt 0.33 2026-02-18 01:34:35 +00:00
Peter Steinberger
31f9be126c style: run oxfmt and fix gate failures 2026-02-18 01:29:02 +00:00
cpojer
d0cb8c19b2 chore: wtf. 2026-02-17 13:36:48 +09:00
Sebastian
ed11e93cf2 chore(format) 2026-02-16 23:20:16 -05:00
cpojer
90ef2d6bdf chore: Update formatting. 2026-02-17 09:18:40 +09:00
Peter Steinberger
544ffbcf7b refactor(extensions): dedupe connector helper usage 2026-02-16 14:59:30 +00:00
Peter Steinberger
743f4b2849 fix(security): harden BlueBubbles webhook auth behind proxies 2026-02-14 19:47:51 +01:00
Peter Steinberger
188c4cd076 fix(security): reject ambiguous webhook target matches 2026-02-14 17:28:28 +01:00
Tyler Yust
45e12d2388 bluebubbles: gracefully handle disabled private API with action/tool filtering and fallbacks (#16002)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 243cc0cc9a
Co-authored-by: tyler6204 <243?+tyler6204@users.noreply.github.com>
Co-authored-by: tyler6204 <64381258+tyler6204@users.noreply.github.com>
Reviewed-by: @tyler6204
2026-02-13 21:15:56 -08:00
Peter Steinberger
f02247b6c5 fix(ci): fix discord proxy websocket binding and bluebubbles timeout status 2026-02-13 19:35:55 +00:00
Peter Steinberger
a1df0939db refactor(bluebubbles): split monitor parsing and processing modules 2026-02-13 19:08:37 +00:00
Peter Steinberger
3cbcba10cf fix(security): enforce bounded webhook body handling 2026-02-13 19:14:54 +01:00
Coy Geek
f836c385ff fix: BlueBubbles webhook auth bypass via loopback proxy trust (#13787)
* fix(an-08): apply security fix

Generated by staged fix workflow.

* fix(an-08): apply security fix

Generated by staged fix workflow.

* fix(an-08): stabilize bluebubbles auth fixture for security patch

Restore the default test password in createMockAccount and add a
fallback password query in createMockRequest when auth is omitted.

This keeps the AN-08 loopback-auth regression tests strict while
preserving existing monitor behavior tests that assume authenticated
webhook fixtures.
2026-02-12 07:12:17 -06:00
max
40b11db80e TypeScript: add extensions to tsconfig and fix type errors (#12781)
* TypeScript: add extensions to tsconfig and fix type errors

- Add extensions/**/* to tsconfig.json includes
- Export ProviderAuthResult, AnyAgentTool from plugin-sdk
- Fix optional chaining for messageActions across channels
- Add missing type imports (MSTeamsConfig, GroupPolicy, etc.)
- Add type annotations for provider auth handlers
- Fix undici/fetch type compatibility in zalo proxy
- Correct ChannelAccountSnapshot property usage
- Add type casts for tool registrations
- Extract usage view styles and types to separate files

* TypeScript: fix optional debug calls and handleAction guards
2026-02-09 10:05:38 -08:00
max
223eee0a20 refactor: unify peer kind to ChatType, rename dm to direct (#11881)
* fix: use .js extension for ESM imports of RoutePeerKind

The imports incorrectly used .ts extension which doesn't resolve
with moduleResolution: NodeNext. Changed to .js and added 'type'
import modifier.

* fix tsconfig

* refactor: unify peer kind to ChatType, rename dm to direct

- Replace RoutePeerKind with ChatType throughout codebase
- Change 'dm' literal values to 'direct' in routing/session keys
- Keep backward compat: normalizeChatType accepts 'dm' -> 'direct'
- Add ChatType export to plugin-sdk, deprecate RoutePeerKind
- Update session key parsing to accept both 'dm' and 'direct' markers
- Update all channel monitors and extensions to use ChatType

BREAKING CHANGE: Session keys now use 'direct' instead of 'dm'.
Existing 'dm' keys still work via backward compat layer.

* fix tests

* test: update session key expectations for dmdirect migration

- Fix test expectations to expect :direct: in generated output
- Add explicit backward compat test for normalizeChatType('dm')
- Keep input test data with :dm: keys to verify backward compat

* fix: accept legacy 'dm' in session key parsing for backward compat

getDmHistoryLimitFromSessionKey now accepts both :dm: and :direct:
to ensure old session keys continue to work correctly.

* test: add explicit backward compat tests for dmdirect migration

- session-key.test.ts: verify both :dm: and :direct: keys are valid
- getDmHistoryLimitFromSessionKey: verify both formats work

* feat: backward compat for resetByType.dm config key

* test: skip unix-path Nix tests on Windows
2026-02-09 09:20:52 +09:00
Tyler Yust
1007d71f0c fix: comprehensive BlueBubbles and channel cleanup (#11093)
* feat(bluebubbles): auto-strip markdown from outbound messages (#7402)

* fix(security): add timeout to webhook body reading (#6762)

Adds 30-second timeout to readBody() in voice-call, bluebubbles, and nostr
webhook handlers. Prevents Slow-Loris DoS (CWE-400, CVSS 7.5).
Merged with existing maxBytes protection in voice-call.

* fix(security): unify Error objects and lint fixes in webhook timeouts (#6762)

* fix: prevent plugins from auto-enabling without user consent (#3961)

Changes default plugin enabled state from true to false in enablePluginEntry().
Preserves existing enabled:true values. Fixes #3932.

* fix: apply hierarchical mediaMaxMb config to all channels (#8749)

Generalizes resolveAttachmentMaxBytes() to use account → channel → global
config resolution for all channels, not just BlueBubbles. Fixes #7847.

* fix(bluebubbles): sanitize attachment filenames against header injection (#10333)

Strip ", \r, \n, and \\ from filenames after path.basename() to prevent
multipart Content-Disposition header injection (CWE-93, CVSS 5.4).
Also adds sanitization to setGroupIconBlueBubbles which had zero filename
sanitization.

* fix(lint): exclude extensions/ from Oxlint preflight check (#9313)

Extensions use PluginRuntime|null patterns that trigger
no-redundant-type-constituents because PluginRuntime resolves to any.
Excluding extensions/ from Oxlint unblocks user upgrades.
Re-applies the approach from closed PR #10087.

* fix(bluebubbles): add tempGuid to createNewChatWithMessage payload (#7745)

Non-Private-API mode (AppleScript) requires tempGuid in send payloads.
The main sendMessageBlueBubbles already had it, but createNewChatWithMessage
was missing it, causing 400 errors for new chat creation without Private API.

* fix: send stop-typing signal when run ends with NO_REPLY (#8785)

Adds onCleanup callback to the typing controller that fires when the
controller is cleaned up while typing was active (e.g., after NO_REPLY).
Channels using createTypingCallbacks automatically get stop-typing on
cleanup. This prevents the typing indicator from lingering in group chats
when the agent decides not to reply.

* fix(telegram): deduplicate skill commands in multi-agent setup (#5717)

Two fixes:
1. Skip duplicate workspace dirs when listing skill commands across agents.
   Multiple agents sharing the same workspace would produce duplicate commands
   with _2, _3 suffixes.
2. Clear stale commands via deleteMyCommands before registering new ones.
   Commands from deleted skills now get cleaned up on restart.

* fix: add size limits to unbounded in-memory caches (#4948)

Adds max-size caps with oldest-entry eviction to prevent OOM in
long-running deployments:
- BlueBubbles serverInfoCache: 64 entries (already has TTL)
- Google Chat authCache: 32 entries
- Matrix directRoomCache: 1024 entries
- Discord presenceCache: 5000 entries per account

* fix: address review concerns (#11093)

- Chain deleteMyCommands → setMyCommands to prevent race condition (#5717)
- Rename enablePluginEntry to registerPluginEntry (now sets enabled: false)
- Add Slow-Loris timeout test for readJsonBody (#6023)
2026-02-07 05:00:55 -08:00
mudrii
5d82c82313 feat: per-channel responsePrefix override (#9001)
* feat: per-channel responsePrefix override

Add responsePrefix field to all channel config types and Zod schemas,
enabling per-channel and per-account outbound response prefix overrides.

Resolution cascade (most specific wins):
  L1: channels.<ch>.accounts.<id>.responsePrefix
  L2: channels.<ch>.responsePrefix
  L3: (reserved for channels.defaults)
  L4: messages.responsePrefix (existing global)

Semantics:
  - undefined -> inherit from parent level
  - empty string -> explicitly no prefix (stops cascade)
  - "auto" -> derive [identity.name] from routed agent

Changes:
  - Core logic: resolveResponsePrefix() in identity.ts accepts
    optional channel/accountId and walks the cascade
  - resolveEffectiveMessagesConfig() passes channel context through
  - Types: responsePrefix added to WhatsApp, Telegram, Discord, Slack,
    Signal, iMessage, Google Chat, MS Teams, Feishu, BlueBubbles configs
  - Zod schemas: responsePrefix added for config validation
  - All channel handlers wired: telegram, discord, slack, signal,
    imessage, line, heartbeat runner, route-reply, native commands
  - 23 new tests covering backward compat, channel/account levels,
    full cascade, auto keyword, empty string stops, unknown fallthrough

Fully backward compatible - no existing config is affected.
Fixes #8857

* fix: address CI lint + review feedback

- Replace Record<string, any> with proper typed helpers (no-explicit-any)
- Add curly braces to single-line if returns (eslint curly)
- Fix JSDoc: 'Per-channel' → 'channel/account' on shared config types
- Extract getChannelConfig() helper for type-safe dynamic key access

* fix: finish responsePrefix overrides (#9001) (thanks @mudrii)

* fix: normalize prefix wiring and types (#9001) (thanks @mudrii)

---------

Co-authored-by: Gustavo Madeira Santana <gumadeiras@gmail.com>
2026-02-04 16:16:34 -05:00
Tyler Yust
8d2f98fb01 Fix subagent announce failover race (always emit lifecycle end + treat timeout=0 as no-timeout) (#6621)
* Fix subagent announce race and timeout handling

Bug 1: Subagent announce fires before model failover retries finish
- Problem: CLI provider emitted lifecycle error on each attempt, causing
  subagent registry to prematurely call beginSubagentCleanup() and announce
  with incorrect status before failover retries completed
- Fix: Removed lifecycle error emission from CLI provider's attempt-level
  .catch() in agent-runner-execution.ts. Errors still propagate to
  runWithModelFallback for retry, but no intermediate lifecycle events
  are emitted. Only the final outcome (after all retries) emits lifecycle
  events.

Bug 2: Hard 600s per-prompt timeout ignores runTimeoutSeconds=0
- Problem: When runTimeoutSeconds=0 (meaning 'no timeout'), the code
  returned the default 600s timeout instead of respecting the 0 setting
- Fix: Modified resolveAgentTimeoutMs() to treat 0 as 'no timeout' and
  return a very large timeout value (30 days) instead of the default.
  This avoids setTimeout issues with Infinity while effectively providing
  unlimited time for long-running tasks.

* fix: emit lifecycle:error for CLI failures (#6621) (thanks @tyler6204)

* chore: satisfy format/lint gates (#6621) (thanks @tyler6204)

* fix: restore build after upstream type changes (#6621) (thanks @tyler6204)

* test: fix createSystemPromptOverride tests to match new return type (#6621) (thanks @tyler6204)
2026-02-02 02:06:14 -08:00
Tyler Yust
9ef24fd400 fix: flush block streaming on paragraph boundaries for chunkMode=newline (#7014)
* feat: Implement paragraph boundary flushing in block streaming

- Added `flushOnParagraph` option to `BlockReplyChunking` for immediate flushing on paragraph breaks.
- Updated `EmbeddedBlockChunker` to handle paragraph boundaries during chunking.
- Enhanced `createBlockReplyCoalescer` to support flushing on enqueue.
- Added tests to verify behavior of flushing with and without `flushOnEnqueue` set.
- Updated relevant types and interfaces to include `flushOnParagraph` and `flushOnEnqueue` options.

* fix: Improve streaming behavior and enhance block chunking logic

- Resolved issue with stuck typing indicator after streamed BlueBubbles replies.
- Refactored `EmbeddedBlockChunker` to streamline fence-split handling and ensure maxChars fallback for newline chunking.
- Added tests to validate new chunking behavior, including handling of paragraph breaks and fence scenarios.
- Updated changelog to reflect these changes.

* test: Add test for clamping long paragraphs in EmbeddedBlockChunker

- Introduced a new test case to verify that long paragraphs are correctly clamped to maxChars when flushOnParagraph is enabled.
- Updated logic in EmbeddedBlockChunker to handle cases where the next paragraph break exceeds maxChars, ensuring proper chunking behavior.

* refactor: streamline logging and improve error handling in message processing

- Removed verbose logging statements from the `processMessage` function to reduce clutter.
- Enhanced error handling by using `runtime.error` for typing restart failures.
- Updated the `applySystemPromptOverrideToSession` function to accept a string directly instead of a function, simplifying the prompt application process.
- Adjusted the `runEmbeddedAttempt` function to directly use the system prompt override without invoking it as a function.
2026-02-02 01:22:41 -08:00
cpojer
f06dd8df06 chore: Enable "experimentalSortImports" in Oxfmt and reformat all imorts. 2026-02-01 10:03:47 +09:00
cpojer
230ca789e2 chore: Lint extensions folder. 2026-01-31 22:42:45 +09:00
cpojer
8cab78abbc chore: Run pnpm format:fix. 2026-01-31 21:13:13 +09:00
Yuri Chukhlib
65dedef65b fix(bluebubbles): debounce by messageId to preserve attachments in text+image messages (#4984)
* fix(bluebubbles): debounce by messageId to preserve attachments in text+image messages

BlueBubbles fires multiple webhook events for a single message - first
without attachment metadata, then ~350ms later with it. Previously,
messages with attachments bypassed debouncing and were processed
immediately, while the text-only version was also queued.

Now the debouncer uses messageId as the key when available, coalescing
all webhook events for the same message. The existing combineDebounceEntries
function merges attachments from all events.

Closes #4848

* fix: increase debounce and handle balloon messages

- Increase DEFAULT_INBOUND_DEBOUNCE_MS from 350ms to 500ms
- Update buildKey to use associatedMessageGuid for balloon messages
- Add regression test for debouncing behavior

Fixes issues identified in code review.

---------

Co-authored-by: Yurii Chukhlib <yurii.chukhlib@viber.com>
Co-authored-by: Tyler Yust <TYTYYUST@YAHOO.COM>
2026-01-30 15:53:14 -08:00
Peter Steinberger
9a7160786a refactor: rename to openclaw 2026-01-30 03:16:21 +01:00