263 Commits

Author SHA1 Message Date
Peter Steinberger 946aa39b50 perf(irc): reuse claimed inbound projection (#127966)
Amp-Thread-ID: https://ampcode.com/threads/T-01a021f5-984a-7628-a30c-491c166ff247

Co-authored-by: Amp <amp@ampcode.com>
2026-08-22 08:44:48 -07:00
Peter Steinberger 992948356f fix(secrets): fail closed for configured references (#127669)
* fix(secrets): fail closed for configured references

Keep explicit SecretRefs authoritative across Gateway auth, onboarding, probes, channel credentials, outbound sends, and GitHub Copilot discovery/embeddings. Ambient credentials remain available only when no reference owns the surface.

* test(secrets): align gateway fallback expectations

* test(providers): remove load-sensitive stream timing

* test(xai): normalize stream capture failures

* fix(copilot): preserve direct auth precedence

* test(ollama): keep progressing streams alive

* test(models): mark resolved config fixtures

* fix(models): drop stale probe import
2026-08-21 18:00:28 -07:00
Peter Steinberger f9b1ccc4fa refactor(plugin-sdk): discriminate delivery and exec results (#124979)
* refactor(plugin-sdk): discriminate delivery and exec results

* test(plugin-sdk): update delivery target expectations

* fix(outbound): preserve canonical delivery targets

* fix(irc): normalize message delivery target
2026-08-16 22:55:41 -07:00
Peter Steinberger 63401b730b fix: keep doctor security conditions as single findings (#124666)
* fix(doctor): record security finding severity

* refactor(security): keep audit severity internal

* fix(security): preserve channel finding severity
2026-08-16 10:24:40 -07:00
Josh Avant 97a53a9b35 feat: audit admitted channel participant identity (#122863)
* feat: audit admitted channel participant identity

* fix: preserve Telegram identity through thread recovery

* fix: signal held gateway process groups

* fix: keep audit evidence passive in collect routing

* fix: validate copied channel participant evidence

* fix: bind channel participant evidence to host ingress

* fix: honor Telegram proof credential roles

* fix: restart held Telegram proof through gateway

* fix: repair channel identity CI regressions

* test(matrix): bind thread routing owner

* fix: preserve direct DM SDK compatibility

* fix: bind channel provenance at host runtime

* test(feishu): provide channel context builder

* fix: defer record-bound channel runtime resolution

* fix: keep channel admission evidence core-private

* fix(audit): bind channel admission to plugin lifecycle

* fix(audit): bind ingress provenance to final context

* refactor(audit): split admission scope keys

* test(queue): cover combined metadata carriers

* refactor(audit): keep lifecycle helpers private

* fix(queue): preserve combined turn authority

* test(channels): provide ingress context builders

* test(channels): align integrated CI fixtures

* test(clickclack): resolve model-loop ingress

* docs: preserve channel participant evidence invariant
2026-08-14 08:57:01 -05:00
Peter Steinberger 99d662473c fix(channels): fail-fast headless channel setup with plugin-declared env contracts (#122530)
* fix(channels): validate headless channel setup

* docs(channels): document headless provisioning

* fix(channels): repair setup metadata typing

* chore(channels): regenerate official channel catalog for env metadata

* fix(slack): keep mode-conditional env contract plugin-owned

Static --use-env declaration keeps only the unconditional SLACK_BOT_TOKEN;
socket-vs-HTTP conditional requirements (app token, signing secret) stay in
Slack's own setup validation so HTTP mode no longer demands an irrelevant
SLACK_APP_TOKEN.

* chore(sdk): regenerate api baselines and catalog after rebase

* fix(slack): align manifest env declaration with runtime contract

* chore(sdk): regenerate api baselines after rebase

* chore(sdk): regenerate api baselines after rebase

* chore(sdk): regenerate api baselines after rebase
2026-08-12 17:12:15 +00:00
Ayaan Zaidi 2c8ed54ddb feat(heartbeat): default delivery to the configured owner, never groups (#121988)
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 13:18:22 +00:00
Peter Steinberger fa03d9b913 refactor: consolidate coercion helpers (#121366)
* refactor: consolidate coercion helpers

* fix: remove duplicate coercion imports

* fix: preserve serialized coercion guard

* chore: ratchet coercion helper carve-outs

* fix(test): keep gauntlet subprocess startup lean

* fix: preserve imported session timestamp semantics

* fix: preserve catalog timestamp string semantics

* chore: align plugin SDK surface ratchet

* fix: preserve trajectory and SDK string contracts

* fix(test): preserve QA record assertion semantics

* fix: complete standalone record guard rename

* refactor(cron): use canonical string coercion

* fix(acpx): preserve Pi timestamp parsing

* test(channels): adapt custody test harnesses

* test(telegram): classify media harness as test support

* test(acpx): split timestamp contract coverage

* test(channels): support generated custody contracts

* chore: ban the full coercion helper name set

Extends the declaration guard to all eleven consolidated helper names and
renames the cron schedule-identity readNumber wrapper to readScheduleInteger
so the banned generic name cannot regrow.

* fix(scripts): repair release-validation guard drift and lint cause

Restores the renamed isJsonRecord guard in assertTrustedWorkflowHarness after
main added isRecord call sites in parallel, and attaches the caught YAML error
as the thrown error cause (preserve-caught-error was red on main).

* fix: preserve Claude timestamp string semantics

* fix: preserve persisted timestamp string semantics

* fix: preserve date-first timestamp contracts

* fix(openai): harden delegation failure formatting

* chore: close coercion helper guard gaps

* test(openai): model non-error delegation rejection

* chore: refresh plugin SDK API contract

* fix(tasks): use canonical string field reader

* fix(ai): use canonical provider error field coercion

* fix(browser): migrate native bootstrap coercion

* docs(plugin-sdk): clarify text record export compatibility

* fix(gateway): normalize approval execution identity

* test(outbound): isolate message action poll harness
2026-08-11 00:02:18 -07:00
Peter Steinberger 8616c0c374 refactor: finish shared test helper migrations (#120996)
* test: finish shared helper migrations

* test: fix helper migration CI

* style: fix test import ordering

* test(acpx): restore deferred void types

* test: fix helper migrations after rebase
2026-08-09 06:00:06 -07:00
Peter Steinberger f9d9d1225a refactor(channels): own the lifecycle status contract in SDK patch factories (#118795)
* refactor(sdk): add channel lifecycle patch factories

* refactor(channels): adopt lifecycle patches in a-m

* refactor(channels): adopt lifecycle patches in n-z

* refactor(runtime): lifecycle-own ambient registries

* test(slack): assert lifecycle factory fields

* fix(sdk): preserve lifecycle patch extras types

* test(zalouser): widen lifecycle status sink

* test(irc): avoid shadowed status patch

* fix(zalo): reuse account-agnostic media route

* fix(gateway): accept explicit channel ready recovery

* test(qa): assert terminal Slack block fact

* test(qa): restore Slack blocked lifecycle scenario

* test(gateway): lock explicit lifecycle recovery contract
2026-08-03 12:39:48 -07:00
Peter Steinberger 0c32879d45 feat(channels): publish recorded lifecycle from bundled channel transitions (#118110)
* feat(channels): publish recorded lifecycle from bundled channel transitions

* chore: remove lifecycle changelog entry
2026-08-02 13:04:49 -07:00
RileyJJY 9a120f364f fix(irc): strip markdown from outbound text (#112961) 2026-08-01 11:55:52 -07:00
Ayaan Gazali d2c9e74702 fix(irc): config validation rejects documented channels.irc.configWrites key (#112392) 2026-07-31 20:46:10 -07:00
Peter Steinberger 9e4381eb1c refactor(channels): unify bundled channel setup contracts (#117106)
* refactor(channels): canonicalize bundled setup contracts

* test(matrix): use scoped environment fixtures
2026-07-31 16:57:29 -07:00
Peter Steinberger 873bcc2985 fix(irc): recognize punctuation in nickname mentions (#116758)
Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 03:21:31 -07:00
Peter Steinberger 986e5d1758 refactor(channels): share ingress retention defaults (#115824) 2026-07-29 06:24:30 -04:00
Peter Steinberger bde6b166c0 refactor(channels): deduplicate ingress monitor shells (#115792)
* refactor(channels): deduplicate ingress monitor shells

* fix(channels): preserve ingress error overload types

* fix(channels): type ingress error overload implementation

* chore(plugin-sdk): ratchet ingress error surface
2026-07-29 05:33:00 -04:00
Peter Steinberger 302f262e6b refactor: deduplicate extension normalization primitives (#115650)
* refactor(plugins): reuse SDK normalization primitives

* fix(ci): repair code-mode matrix checks

* fix(ci): satisfy code-mode matrix gates

* fix(ci): use matrix evidence export

* fix(ci): validate matrix evidence artifact
2026-07-29 04:10:06 -04:00
wahaha1223 0245f2c601 fix(irc): hide tool traces from inbound replies (#109012)
* fix(irc): hide tool traces from inbound replies

* test(irc): type async reply dispatch mock

* test(irc): type reply sender mock

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-29 00:55:12 -04:00
Peter Steinberger 08b1ff73e2 refactor(channels): fix account resolution and setup isolation (#114395)
* refactor(channels): fix account resolution and setup isolation

* fix(channels): keep account resolvers safely destructurable
2026-07-27 03:49:37 -04:00
Peter Steinberger 83307f6bc7 refactor(irc): delegate outbound sends to message adapter (#114235) 2026-07-26 22:10:51 -04:00
Peter Steinberger 446816f751 test: table-drive followup scenarios, centralize sanitizer wiring, remove obsolete negatives (#113583)
* test(auto-reply): table-drive followup runner scenarios

* test(channels): centralize sanitizer wiring coverage

* test: remove obsolete negative coverage
2026-07-25 02:14:44 -07:00
Jesse Merhi 4a2a600809 feat(channels): add channel-owned setup contracts (#112176)
* feat(channels): add channel-owned setup contracts

* test(channels): align legacy setup fixtures

* chore(channels): regenerate config and SDK baselines after rebase

* fix(update): run fresh doctor after current-process core changes

* fix(channels): align add pre-scan with execution precedence

* style(cli): format channels-cli test additions

* fix(channels): restore option-before-positional channel resolution via metadata arity scan

* fix(channels): keep help flags out of metadata arity escalation

* test(update): mock fresh post-update doctor in current-process suites

* style: format review fixes and correct entrypoint mock type

* fix(channels): register only modern contract options for dual-publishing plugins

* test(update): align downgrade suites with fresh-doctor child invocation

* docs(channels): record empty-contract and input-forwarding invariants

* fix(line): keep the shipped --token switch as a channel access token alias

* fix(signal): stop treating exact cross-family loopback endpoints as bind-aligned

* chore(config): regenerate docs config baselines after second rebase

* style: format rebased channels add tests

* fix(channels): enforce field-key and flag-name agreement in setup contracts

* fix(signal): detect container endpoints for bare --http-url setup

* fix(signal): ignore unconfigured accounts in transport collision checks

* fix(channels): validate negated setup flags in contract and normalizer

* fix(signal): preserve existing transport kind when setup detection is unreachable

* style(signal): use direct boolean check in collision guard

* style(signal): type test config literals

* docs(update): record two-read design of fresh-doctor validation gate

* fix(channels): satisfy post-rebase architecture gates

* docs: refresh channel setup map

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-22 19:57:42 -04:00
Peter Steinberger e1d5d3dc16 refactor(channels): move single-account promotion keys to plugin declarations (#112293)
* refactor(channels): move single-account promotion keys to plugin declarations

* fix(channels): keep legacy promotion tier for undeclared setup adapters

* fix(channels): keep plugin discovery lazy in setup promotion helpers

* fix(channels): resolve bundled promotion surfaces from setup-only artifacts

* test(matrix): use vi.stubEnv in setup test env helper
2026-07-21 06:48:30 -07:00
Peter Steinberger ca3a1873a6 fix(test): make broad shard execution deterministic (#111416)
* fix(test): harden broad shard execution

* test: allow broad-profile contention

* style(telegram): remove redundant chat id cast

* style(test): preserve setup test spacing
2026-07-19 08:39:16 -07:00
Peter Steinberger 7562b79465 refactor(channels): share durable ingress monitor (#111017)
* refactor(channels): share durable ingress monitor

* fix(channels): harden ingress monitor shutdown

* docs: refresh SDK documentation map
2026-07-19 00:42:08 +01:00
Peter Steinberger 3dd26e7d9c feat(irc): add durable ingress queue (#110914)
* feat(irc): add durable ingress queue

* fix(irc): preserve ingress connection context

* fix(irc): accept colonless privmsg bodies

* fix(irc): pause ingress during disconnects

* fix(irc): ignore self echoes in activity

* fix(irc): bind private ingress to connections
2026-07-18 20:19:19 +01:00
Peter Steinberger 3e74534a70 fix(channels): keep healthy accounts running when credential files fail (#110360)
* fix(channels): isolate unavailable credential files

* fix(channels): complete credential file type contract

* fix(channels): preserve credential path handling

* test(channels): cover credential file edge cases

* fix(channels): ignore blank credential file paths

* test(secrets): keep empty credential files unavailable

* test(googlechat): keep temp cleanup plugin-local

* fix(channels): keep credential diagnostic internal

* chore: defer release note to release automation
2026-07-18 06:27:59 +01:00
Peter Steinberger 31ed59bdf6 feat: keep main agents aware of group activity (#110332)
* feat(sessions): auto-watch group activity from main

* feat(sessions): allow watchers to read watched sessions

* fix(sessions): skip group watches for system turns

* fix(sessions): preserve watched session semantics

* fix(channels): propagate effective dm scope

* fix(sessions): canonicalize main watch identity

* fix(sessions): distinguish ambient group watches

* fix(sessions): revoke ineligible ambient watches

* chore: drop CHANGELOG edit (release generation owns it)
2026-07-18 04:06:58 +01:00
Peter Steinberger 662e79d574 test(release): harden extension prerelease contracts 2026-07-17 13:43:12 +01:00
Peter Steinberger 4ecd85e0b7 fix: keep healthy channel accounts running when one credential fails (#109766)
* fix(secrets): isolate channel account credentials

* fix(secrets): normalize Feishu default ownership

* fix(feishu): preserve accountless secret ownership

* fix(matrix): keep accountless top-level refs active
2026-07-17 01:12:18 -07:00
Peter Steinberger 0e792b6de3 refactor(channels): centralize inbound orchestration and remove internal compat (#109716)
* refactor(channels): centralize inbound turn orchestration

* refactor(runtime): remove stale compatibility paths

* chore(guards): reject internal deprecated API use

* refactor(channels): simplify core turn planning

* chore(guards): keep deprecated checks boundary-focused

* refactor(memory): keep modern config off compat barrel

* fix(msteams): preserve feedback learning

* test(channels): align modern inbound fixtures

* refactor(channels): finish modern inbound migration

* refactor(channels): tighten core inbound kernel

* fix(channels): preserve turn assembly narrowing

* test(sdk): keep runtime mock binding immutable

* test(matrix): isolate read policy runtime

* test(msteams): mock canonical reply factory

* test(slack): mock core inbound turn dispatch

* test(telegram): inject core session recorder

* test(signal): inject core session recorder

* test(googlechat): assert canonical inbound routing

* test(synology-chat): align core turn fixture

* fix(sdk): preserve direct DM runtime compat

* refactor(channels): own inbound envelope compat in core

* refactor(channels): trim inbound dispatch seams

* refactor(channels): remove redundant async wrappers

* test(synology-chat): type canonical dispatcher mock

* refactor(channels): remove remaining dead compat seams

* chore(sdk): refresh API baseline after rebase

* fix(channels): preserve direct DM identity metadata
2026-07-17 00:56:46 -07:00
Peter Steinberger 44314c9451 refactor(channels): table-drive setup and control-plane helpers (#109028)
* refactor(channels): table-drive setup credentials, config schemas, doctor detectors, status issues

* refactor(channels): control-plane tables (rest)

* fix(plugin-sdk): refresh surface budgets after rebase

* style(plugin-sdk): format credential helpers

* fix(channels): preserve generic schema refinement types

* fix(channels): preserve schema input and output inference

* fix(clickclack): forward credential patch fields

* test(plugin-sdk): use redacted credential fixtures

* fix(plugin-sdk): refresh API baseline after inference fix

* fix(channels): keep multi-account envelope optional

* fix(channels): preserve policy validation invariants

* fix(extensions): remove stale awaits from file checks

* style(qqbot): format synchronous file check

* fix(config): refresh bundled channel metadata

* fix(qqbot): reject unsupported pairing policy
2026-07-16 07:17:01 -07:00
Peter Steinberger 074f07c520 refactor(irc,workboard): trim internal exports (#107749)
* refactor(irc): privatize internal plugin surfaces

* refactor(workboard): privatize internal plugin surfaces

* chore(deadcode): refresh unused-export baseline
2026-07-14 13:19:08 -07:00
Peter Steinberger b34bba8e57 refactor(channels): migrate the remaining nine group-policy walkers onto the scope tree (#107181)
* refactor(channels): migrate irc, nextcloud-talk, zalouser, slack, and matrix group policy onto the scope tree

* fix(irc): drop unused tree binding in group match

* fix(zalouser): keep wildcard lookup opt-in for explicit-only candidates

* fix(zalouser): adapter opts into the wildcard fallback candidate

* refactor(channels): migrate feishu, msteams, and discord group policy onto the scope tree and drop the core discord duplicate

* fix(msteams): restore the cross-team scan fallback for policy-less matched teams

* fix(feishu): keep the adapter's zod-typed group config for the scope tree

* revert(core): keep the discord require-mention fallback until stage 3 makes it provably redundant

* chore(matrix): drop the now-unused channel entry match re-export

* refactor(telegram): migrate group policy onto the scope tree

* test(telegram): keep the bot token fixture off secret-scanner patterns

* test(telegram): use a computed key for the bot token fixture

* test(telegram): assemble the bot token fixture indirectly for scanner and lint
2026-07-13 23:56:35 -07:00
Peter Steinberger 83bf0379c9 refactor(channels): share plugin contract scaffolds (#105838)
* refactor(channels): share plugin contract scaffolds

* fix: preserve channel config hint metadata

* chore: refresh Plugin SDK API baseline

* ci: refresh plugin SDK surface budget

* fix(ci): allow read-only state database boundary

* fix(ci): dedupe read-only state database boundary
2026-07-13 01:51:32 -07:00
Peter Steinberger 4a05a087a1 refactor(config): retire flat streaming keys from the last six channel schemas via doctor migration (#105709)
* refactor(config): retire flat streaming keys from the last six channel schemas

signal, irc, nextcloud-talk, whatsapp, googlechat, and mattermost now accept
only the nested streaming.{chunkMode,block.enabled,block.coalesce} shape
(mattermost also drops scalar/boolean streaming); flat spellings migrate via
each channel's defineChannelAliasMigration doctor contract with root seeding
for their wholesale-replace account merges.

* feat(channels): warn once per key when the deprecated flat streaming fallback is used

Bundled schemas now reject the flat delivery keys, so the streaming.ts
fallback only serves external SDK plugin configs; emit a once-per-process
per-key subsystem warning and pin the removal plan to the next release train.

* chore(config): regenerate bundled channel config metadata for nested-only streaming

* docs: describe nested-only channel streaming config and the SDK flat-key deprecation window

* fix(whatsapp): seed migrated named-account streaming from the accounts.default layer

WhatsApp resolution layers accounts.default shared config between root and
named accounts, so doctor-materialized account streaming objects now inherit
default-account settings over root ones; mattermost schema test moves to the
nested-only shape with explicit rejection coverage; scope flat-key deprecation
notes to the pending Matrix/Feishu migrations.

* fix(whatsapp): resolve the default account case-insensitively when seeding migrated streaming

* chore(plugin-sdk): repin API baseline for nested-only channel streaming types
2026-07-12 15:27:28 -07:00
Vincent Koc c25cd32c28 refactor(plugins): trim private helper exports (#105687) 2026-07-12 22:26:14 +02:00
Peter Steinberger e34bff2276 refactor(plugin-sdk): collapse internal config-schema facade usage onto one module (#104665) 2026-07-11 13:00:53 -07:00
Peter Steinberger a2a68d154b fix(agent): preserve explicit recipient sessions (#101507)
* fix(agent): honor recipient session routing

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: pingfanfan <pingfan.work@gmail.com>

* fix(agent): preserve canonical recipient routes

* fix(agent): require exact recipient routes

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>

Co-authored-by: pingfanfan <pingfan.work@gmail.com>

* fix(agent): harden recipient route resolution

* fix(imessage): require canonical recipient handles

* fix(agent): refine provider recipient exactness

* fix(agent): bound direct alias session routing

* fix(signal): preserve direct alias route type

* fix(agent): honor binding-scoped recipient sessions

* fix(routing): honor configured main session aliases

* fix(clickclack): align account-owned session routes

* fix(twitch): preserve canonical recipient sessions

* fix(routing): isolate stable outbound identities

* chore: defer recipient session changelog

* fix(sms): use dedicated channel SDK facade

---------

Co-authored-by: vincentkoc <25068+vincentkoc@users.noreply.github.com>
Co-authored-by: pingfanfan <pingfan.work@gmail.com>
2026-07-07 10:07:13 +01:00
xingzhou 5aa7c6267c fix(irc): monitor stays disconnected after IRC socket closes (#100799) 2026-07-06 06:07:12 -07:00
Yuval Dinodia 89881b6611 fix(irc): long non-ASCII messages are silently truncated at the 512-byte line limit (#99138)
* fix(irc): chunk PRIVMSG by UTF-8 byte budget so long non-ASCII messages are not truncated

IRC caps a full protocol line at 512 bytes, but sendPrivmsg split outbound
text by UTF-16 code units against the 350 char default. Multi-byte text such
as CJK or emoji produced lines far beyond 512 bytes and servers silently
dropped the tail of every oversized chunk. The chunker now also enforces a
UTF-8 byte budget derived from the line framing overhead, splitting on code
point boundaries and preferring spaces, while ASCII chunking is unchanged.

* test(irc): move loopback IRC server helper to shared test helpers

The colocated node:net import tripped the network-runtime-boundary PR diff
scan for extensions/irc/src. The loopback server now lives in test/helpers,
outside the scanned network runtime paths, and the CJK, emoji, and ASCII
chunking cases keep driving the real client over a real TCP socket.

* fix(irc): decouple byte budget from character cap and move loopback helper to irc test-support

The byte budget is now derived only from the 512-byte line limit and framing
overhead, independent of messageChunkMaxChars, so low character caps with
multibyte text keep advancing instead of dropping the message. The loopback
IRC server helper moves from test/helpers to the extension-local package-root
test-support surface so extension tests stay off repo helper bridges and raw
socket use stays outside extensions/irc/src.

* fix(irc): enforce UTF-8 wire limits

* test(irc): satisfy loopback harness lint

* test(irc): avoid implicit Promise return

* test(irc): handle loopback close errors

* fix(irc): preserve boundary word splitting

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-05 22:10:59 +01:00
llagy009 685c22dfb7 fix(irc): chunk PRIVMSG on UTF-16 boundary to avoid lone surrogates (#96572)
* fix(irc): chunk PRIVMSG on UTF-16 boundary to avoid lone surrogates

sendPrivmsg split long messages with String.slice on a UTF-16 code-unit
index, so an emoji (or other astral character) straddling the split
point was cut into a lone high/low surrogate, sending broken bytes in
the PRIVMSG to the IRC server.

Slice each chunk with sliceUtf16Safe so a surrogate pair is never split.
When the budget is too small to fit even the leading astral character,
emit that character whole so chunking still makes progress.

Adds a socket-level test (fake net/tls socket) asserting no PRIVMSG
chunk contains a lone surrogate, including the one-code-unit budget
edge case, while the existing space-preferring split is preserved.

* refactor(irc): make surrogate-safe chunking direct

* fix(irc): preserve one-unit chunk limits

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-05 12:14:05 -07:00
Dallin Romney 59b08b4693 refactor(shared): consolidate remaining channel lazy loaders (#99302) 2026-07-02 19:08:11 -07:00
Yuval Dinodia acb0e8e88d fix(irc): classify host-less nick!user allowlist entries as mutable
An IRC sender mask is nick!user@host where only host is server verified;
nick and user (ident) are client supplied and spoofable. The allowlist
identity classifier treated any entry containing "!" or "@" as a verified
stable identity, so a host-less nick!user entry was classified stable and
matched by the host-less nick!user subject candidate. With
dangerouslyAllowNameMatching at its secure default (off), the mutable
identifier policy only strips entries owned by a dangerous field, so the
host-less entry was never stripped and a remote sender presenting the same
nick and ident was admitted regardless of host.

Require a verified @host component before an entry or subject is classified
stable. Host-less nick and host-less nick!user are now both routed to a
dangerous (mutable) field so they are gated by the same name-matching policy.
The doctor mutable-allowlist detector now also flags host-less nick!user
entries so operators who typed that undocumented shape get a warning. The
documented full nick!user@host mask stays stable and unaffected.
2026-07-01 04:01:05 -07:00
llagy009 2b77862b92 fix(irc): guard surrogate-range codepoints in \u literal-escape decoder (#97683)
Two-step decode in decodeLiteralEscapes:
- Step 1: regex anchored to high-surrogate range (U+D800–U+DBFF) so a
  preceding BMP escape (e.g. \u0041) cannot consume the high-surrogate
  half of a valid pair like \uD83D\uDE00 (😀), leaving \uDE00 lone.
- Step 2: decode remaining BMP codepoints; preserve lone surrogates as
  six-character literals instead of corrupting them to U+FFFD in the
  outbound IRC UTF-8 stream.
2026-06-30 17:46:57 -07:00
wendy 6ce88ca51d fix(irc): prevent ghost nick collisions on rejoin after network delay (#96108)
* fix(irc): prevent ghost nick collisions on rejoin after network delay

* test(irc): add regression tests for fallback nick uniqueness

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

---------

Co-authored-by: wendy-chsy <wan.wenyan@xydigit.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-06-28 10:47:39 -07:00
Masato Hoshino ddedf13190 fix(irc): sanitize internal tool-trace lines from outbound text (#97214)
* fix(irc): sanitize internal tool-trace lines from outbound text

* fix(irc): sanitize internal tool-trace lines from outbound text

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-06-27 08:34:38 -07:00
Vincent Koc 95093303c8 chore(deadcode): remove test-only channel helpers 2026-06-22 16:57:03 +08:00
Vincent Koc e552f97866 refactor(irc): drop unused text splitter 2026-06-19 12:36:41 +08:00