Commit Graph

253 Commits

Author SHA1 Message Date
Peter Steinberger
a7f6c95675 perf(test): consolidate slack monitor suites 2026-02-15 23:14:42 +00:00
Peter Steinberger
023091ded3 perf(test): consolidate slack tool-result suites 2026-02-15 23:14:42 +00:00
Peter Steinberger
4fc72226fa perf(test): speed up slack slash suite 2026-02-15 23:14:42 +00:00
Peter Steinberger
a91553c7cf perf(slack): consolidate slash tests 2026-02-15 23:14:42 +00:00
Peter Steinberger
1f1e97674f refactor(allowlists): share user entry collection 2026-02-15 17:45:16 +00:00
Shadow
c6b3736fe7 fix: dedupe probe/token base types (#16986) (thanks @iyoda) 2026-02-15 11:36:54 -06:00
Shadow
b6069fc68c feat: support per-channel ackReaction config (#17092) (thanks @zerone0x) 2026-02-15 11:30:25 -06:00
Peter Steinberger
25be51967a refactor(channels): share allowlist resolution summary 2026-02-15 17:26:27 +00:00
Peter Steinberger
da2fde7b6f refactor(slack): share room context hints 2026-02-15 17:06:17 +00:00
Sebastian
6277698f86 test(discord): fix updated test harness mocks 2026-02-15 11:50:16 -05:00
Peter Steinberger
afc333cc5b refactor(slack): dedupe event system-event emit 2026-02-15 16:01:20 +00:00
Peter Steinberger
30eacd36af refactor(test): dedupe slack slash mocks 2026-02-15 15:57:33 +00:00
Mr. Guy
e927fd1e35 fix: allow agent workspace directories in media local roots (#17136)
Merged via /review-pr -> /prepare-pr -> /merge-pr.

Prepared head SHA: 7545ef1e19
Co-authored-by: MisterGuy420 <255743668+MisterGuy420@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
2026-02-15 10:53:45 -05:00
Peter Steinberger
723e314e2b fix(ci): avoid vitest TDZ in shared mocks 2026-02-15 14:52:41 +00:00
Peter Steinberger
6ec76af3a6 refactor(test): share slack slash mocks 2026-02-15 14:41:45 +00:00
Peter Steinberger
9d7113c74c refactor(channels): share allowlist config patch helper 2026-02-15 13:44:00 +00:00
Peter Steinberger
628c7b2398 refactor(slack): dedupe allowlist match selection 2026-02-15 05:57:11 +00:00
Peter Steinberger
f41f6d3243 refactor(channels): share allowlist user resolve helpers 2026-02-15 05:28:46 +00:00
Vignesh Natarajan
6d0cd54ac1 Slack: bound thread starter cache growth 2026-02-14 17:55:25 -08:00
Peter Steinberger
2ff5c17917 refactor(slack): dedupe member join/leave handlers 2026-02-15 01:15:43 +00:00
Peter Steinberger
f835eb32f3 refactor(slack): share message action helpers 2026-02-15 01:15:43 +00:00
Peter Steinberger
8181f51dbd perf(test): reuse temp root in slack prepare contract suite 2026-02-15 00:45:10 +00:00
Peter Steinberger
61371a712b refactor(test): share slack monitor helpers 2026-02-14 23:51:42 +00:00
Peter Steinberger
aae290eed3 refactor(test): dedupe slack inbound contract setup 2026-02-14 22:14:35 +00:00
Peter Steinberger
808ec68e41 refactor(test): reuse slack slash harness 2026-02-14 22:09:12 +00:00
Peter Steinberger
6aab89939f refactor(slack): dedupe pin event handlers 2026-02-14 21:42:54 +00:00
Peter Steinberger
2f67564c93 refactor(test): dedupe slack inbound contract prep 2026-02-14 20:21:17 +00:00
Peter Steinberger
9d0a1e32bb test: cover Slack/Discord dmPolicy aliases in monitor 2026-02-14 21:04:27 +01:00
Peter Steinberger
47b6cde8ca refactor(config): add dmPolicy aliases for Slack/Discord 2026-02-14 21:04:27 +01:00
Peter Steinberger
f19eabee54 fix(slack): gate DM slash command authorization 2026-02-14 19:10:29 +01:00
Hudson
1d6abddb9f fix(signal): outbound formatting and markdown IR rendering improvements (#9781)
* fix: Signal and markdown formatting improvements

Markdown IR fixes:
- Fix list-paragraph spacing (extra newline between list items and following paragraphs)
- Fix nested list indentation and newline handling
- Fix blockquote_close emitting redundant newline (inner content handles spacing)
- Render horizontal rules as visible ─── separator instead of silent drop
- Strip inner cell styles in code-mode tables to prevent overlapping with code_block span

Signal formatting fixes:
- Normalize URLs for dedup comparison (strip protocol, www., trailing slash)
- Render headings as bold text (headingStyle: 'bold')
- Add '> ' prefix to blockquotes for visual distinction
- Re-chunk after link expansion to respect chunk size limits

Tests:
- 51 new tests for markdown IR (spacing, lists, blockquotes, tables, HR)
- 18 new tests for Signal formatting (URL dedup, headings, blockquotes, HR, chunking)
- Update Slack nested list test expectation to match corrected IR output

* refactor: style-aware Signal text chunker

Replace indexOf-based chunk position tracking with deterministic
cursor tracking. The new splitSignalFormattedText:

- Splits at whitespace/newline boundaries within the limit
- Avoids breaking inside parentheses (preserves expanded link URLs)
- Slices style ranges at chunk boundaries with correct local offsets
- Tracks position via offset arithmetic instead of fragile indexOf

Removes dependency on chunkText from auto-reply/chunk.

Tests: 19 new tests covering style preservation across chunk boundaries,
edge cases (empty text, under limit, exact split points), and integration
with link expansion.

* fix: correct Signal style offsets with multiple link expansions

applyInsertionsToStyles() was using original coordinates for each
insertion without tracking cumulative shift from prior insertions.
This caused bold/italic/etc styles to drift to wrong text positions
when multiple markdown links expanded in a single message.

Added cumulative shift tracking and a regression test.

* test: clean up test noise and fix ineffective assertions

- Remove console.log from ir.list-spacing and ir.hr-spacing tests
- Fix ir.nested-lists.test.ts: remove ineffective regex assertion
- Fix ir.hr-spacing.test.ts: add actual assertions to edge case test

* refactor: split Signal formatting tests (#9781) (thanks @heyhudson)

---------

Co-authored-by: Hudson <258693705+hudson-rivera@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 16:57:20 +01:00
Peter Steinberger
50645b905b refactor(outbound): centralize outbound identity 2026-02-14 16:44:43 +01:00
Robby
09e1cbc35d fix(cron): pass agent identity through delivery path (#16218) (#16242)
* fix(cron): pass agent identity through delivery path

Cron delivery messages now include agent identity (name, avatar) in
outbound messages. Identity fields are passed best-effort for Slack
(graceful fallback if chat:write.customize scope is missing).

Fixes #16218

* fix: fix Slack cron delivery identity (#16242) (thanks @robbyczgw-cla)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 16:08:51 +01:00
Peter Steinberger
4b1cadaecb refactor(media): normalize inbound media type defaults (#16228) 2026-02-14 15:06:13 +01:00
Tanwa Arpornthip
c76288bdf1 fix(slack): download all files in multi-image messages (#15447)
* fix(slack): download all files in multi-image messages

resolveSlackMedia() previously returned after downloading the first
file, causing multi-image Slack messages to lose all but the first
attachment. This changes the function to collect all successfully
downloaded files into an array, matching the pattern already used by
Telegram, Line, Discord, and iMessage adapters.

The prepare handler now populates MediaPaths, MediaUrls, and
MediaTypes arrays so downstream media processing (vision, sandbox
staging, media notes) works correctly with multiple attachments.

Fixes #11892, #7536

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

* fix(slack): preserve MediaTypes index alignment with MediaPaths/MediaUrls

The filter(Boolean) on MediaTypes removed entries with undefined contentType,
shrinking the array and breaking index correlation with MediaPaths and MediaUrls.
Downstream code (media-note.ts, attachments.ts) requires these arrays to have
equal lengths for correct per-attachment MIME type lookup. Replace filter(Boolean)
with a nullish coalescing fallback to "application/octet-stream".

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

* fix(slack): align MediaType fallback and tests (#15447) (thanks @CommanderCrowCode)

* fix: unblock plugin-sdk account-id typing (#15447)

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-14 14:16:02 +01:00
Peter Steinberger
54a242eaad perf(test): gate monitor runtime logs during vitest 2026-02-14 01:14:56 +00:00
Peter Steinberger
3cbcba10cf fix(security): enforce bounded webhook body handling 2026-02-13 19:14:54 +01:00
Marcus Castro
b3b49bed80 fix(slack): override video/* MIME to audio/* for voice messages (#14941)
* fix(slack): override video/* MIME to audio/* for voice messages

* fix(slack): preserve overridden MIME in return value

* test(slack): fix media monitor MIME mock wiring

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-13 18:09:04 +01:00
Peter Steinberger
faec6ccb1d perf(test): reduce module reload churn in unit suites 2026-02-13 15:19:13 +00: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
Peter Steinberger
a43136c85e fix: align slack thread footer metadata with reply semantics (#14625) (thanks @bennewton999) 2026-02-13 05:18:06 +01:00
Ben Newton
2b9d5e6e30 feat(slack): include thread metadata (thread_ts, parent_user_id) in agent context
Adds thread_ts and parent_user_id to the Slack message footer for thread
replies, giving agents awareness of thread context. Top-level messages
remain unchanged.

Includes tests verifying:
- Thread replies include thread_ts and parent_user_id in footer
- Top-level messages exclude thread metadata
2026-02-13 05:18:06 +01:00
CHISEN Kaoru
e25ae55879 fix(discord): replyToMode first behaviour 2026-02-12 18:50:36 -06:00
0xRain
626a1d0699 fix(gateway): increase WebSocket max payload to 5 MB for image uploads (#14486)
* fix(gateway): increase WebSocket max payload to 5 MB for image uploads

The 512 KB limit was too small for base64-encoded images — a 400 KB
image becomes ~532 KB after encoding, exceeding the limit and closing
the connection with code 1006.

Bump MAX_PAYLOAD_BYTES to 5 MB and MAX_BUFFERED_BYTES to 8 MB to
support standard image uploads via webchat.

Closes #14400

* fix: align gateway WS limits with 5MB image uploads (#14486) (thanks @0xRaini)

* docs: fix changelog conflict for #14486

---------

Co-authored-by: 0xRaini <0xRaini@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-02-12 17:48:49 +01:00
NM
3696b15abc fix(slack): change default replyToMode from "off" to "all" (#14364)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-02-12 00:13:07 -06:00
J young Lee
2aa9570465 fix(slack): detect control commands when message starts with @mention (#14142)
Merged via /review-pr-v2 -> /prepare-pr-v2 -> /merge-pr-v2.

Prepared head SHA: cb0b4f6a3b
Co-authored-by: beefiker <55247450+beefiker@users.noreply.github.com>
Co-authored-by: gumadeiras <gumadeiras@gmail.com>
Reviewed-by: @gumadeiras
2026-02-11 11:41:48 -05:00
Peter Steinberger
53273b490b fix(auto-reply): prevent sender spoofing in group prompts 2026-02-10 00:44:38 -06:00
max
8d75a496bf refactor: centralize isPlainObject, isRecord, isErrno, isLoopbackHost utilities (#12926) 2026-02-09 17:02:55 -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
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