Commit Graph

1287 Commits

Author SHA1 Message Date
Peter Steinberger
9a3a5ae818 refactor(markdown): formalize channel profiles (#113002) 2026-07-23 06:16:06 -04:00
Peter Steinberger
7497147519 refactor: hoist channel doctor migration helpers (#112782) 2026-07-22 22:53:39 -04: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
637afd0114 refactor(config): move Slack and Signal schemas to plugins (#112792)
* refactor(config): move Slack and Signal schemas to plugins

* build(signal): declare schema runtime dependency

* build(signal): refresh plugin shrinkwrap
2026-07-22 19:14:56 -04:00
Peter Steinberger
1650faf6a1 refactor(plugin-sdk): remove unread setup helpers (#112767)
* refactor(plugin-sdk): remove unread setup helpers

* refactor(plugin-sdk): privatize removed setup edges

* test(channels): remove stale proxy mock import
2026-07-22 14:57:32 -07:00
Peter Steinberger
88875ab7d9 refactor(channels): remove retired ack cleanup branches (#112635) 2026-07-22 03:54:28 -07:00
Peter Steinberger
5e651d5ac7 fix(channels): restore assistant context after restart (#112548)
* fix(channels): restore transcript context after restart

Merge bounded active-branch session transcript turns at the shared prepared-turn seam so message channels retain assistant replies after restart or history eviction. Migrate Telegram's one-off merge while preserving exact projection and legacy dedupe behavior.\n\nCloses #112520. Slack case reported by Joe Tam (@joetam) in #102594.

* style(channels): avoid spread in transcript mapping

* refactor(telegram): drop obsolete transcript exports
2026-07-21 23:45:01 -07:00
Peter Steinberger
f236e83de7 refactor(channels): shrink ChannelSetupInput to a generic envelope with a deprecated compatibility tier (#112319)
* refactor(channels)!: shrink ChannelSetupInput to a generic envelope with a deprecated compatibility tier

* fix(channels): keep ChannelSetupInput structurally assignable without an index signature

* docs: regenerate docs map
2026-07-21 22:17:03 -07:00
Peter Steinberger
edecdbd05e refactor(config): config-surface reduction tranche 3 — product consolidations (review request) (#111527)
* refactor(config): consolidate media model lists

* refactor(config): unify memory configuration

* refactor(config): consolidate TTS ownership

* refactor(config): move typing policy to agents

* refactor(config): retire product-level config surfaces

* refactor(config): share scoped tool policy type

* chore(config): refresh generated baselines

* fix(config): honor agent typing overrides

* fix(config): migrate sibling config consumers

* refactor(infra): keep base64url decoder private

* fix(config): strip invalid legacy TTS values

* chore(config): refresh rebased baseline hash

* fix(doctor): route legacy messages.tts.realtime voice to talk during tts move

* refactor(config): polish final layout names

* refactor(config): freeze retired tuning defaults

* feat(config): add fast mode default symmetry

* refactor(config): key agent entries by id

* docs(config): update final layout reference

* test(config): cover final layout migrations

* chore(config): refresh final layout baselines

* fix(config): align final layout runtime readers

* fix(config): align remaining readers

* fix(config): stabilize final layout migrations

* fix(config): finalize config projection proof

* fix(config): address final layout review

* docs(release): preserve historical config names

* fix(config): complete keyed agent migration

* fix(config): close final migration gaps

* fix(config): finish full-branch review

* fix(config): complete runtime secret detection

* fix(config): close final review findings

* fix(config): finish canonical docs and heartbeat migration

* fix(config): integrate latest main after rebase

* refactor(env): isolate test-only controls

* refactor(env): isolate build and development controls

* refactor(env): collapse process identity indirection

* refactor(env): remove duplicate config and temp aliases

* docs(env): define the operator-facing allowlist

* ci(env): ratchet production variable count

* fix(env): remove stale provider helper import

* fix(env): make ratchet sorting explicit

* test(env): keep test seam in dead-code audit

* test(env): cover ratchet growth and boundary; document surface budgets

* docs(config): document tier-eval consolidations

* docs(config): clarify speech preference ownership

* test(memory): align retired tuning fixtures

* refactor(memory): freeze engine heuristics

* refactor(config): apply tier-eval tranche

* refactor(tts): move persona shaping to providers

* refactor(compaction): move prompt policy to providers

* test(config): align hookified prompt fixtures

* chore(deadcode): classify test-only exports

* chore(github): remove unused spawn helper

* chore(deadcode): classify queue diagnostics

* chore(deadcode): remove unused lane snapshot export

* chore(plugin-sdk): ratchet consolidated surface

* fix(config): integrate latest main after rebase
2026-07-21 20:28:43 -07:00
Peter Steinberger
9b3b84a18b feat(slack): support Agent View (#103895)
* feat(slack): support Agent View

* fix(slack): align Agent View with current runtime

* fix(slack): keep suggested prompt type internal

* fix(slack): gate Agent View persistence on capability

* chore: leave Slack Agent View notes to release generation

* fix(slack): gate Agent View persistence on capability

* test(slack): use neutral bot token fixture

* feat(slack): support Agent View

* feat(slack): support Agent View

---------

Co-authored-by: Patrick Erichsen <patrick.a.erichsen@gmail.com>
2026-07-21 19:37:49 -07: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
94557e1c57 refactor(media): ingress and preflight consumers ride ordered media facts (#112276)
* refactor(media): ingress and preflight consumers ride ordered media facts

Media-facts program PR 2: channel context retains ordered MediaFact[]
through finalization; inbound gates, hook mapping, current-turn
normalization, and all five channel families' audio preflights consume
facts. Four review cycles hardened the canonical/legacy merge: canonical
facts are authoritative (legacy fills gaps only), one shared staging
predicate covers MediaStaged and MediaWorkspaceDir, kind inference runs
after fallback MIME fills, legacy counts use max array cardinality, and
alignment-only empty slots no longer count as media presence — all
pinned by a 54-cell canonical×legacy×type merge-matrix test.

* fix(media): hide internal fact helper
2026-07-21 05:32:49 -07:00
Peter Steinberger
d725632ee7 refactor(channels): move channel-flavored setup flags into plugin manifests (#112239)
* refactor(channels): move channel-flavored setup flags into plugin manifests

* refactor(channels): normalize manifest cliAddOptions formatting, drop changelog entry

* fix(channels): dedupe channels add options by commander switch identity

* fix(channels): let the selected channel's cliAddOptions win switch dedupe
2026-07-21 02:50:19 -07:00
Dallin Romney
cde2e71d43 fix(deps): update axios past proxy advisory (#111984) 2026-07-21 09:21:14 +09:00
Peter Steinberger
c7e7ac2728 refactor: remove expired plugin compatibility surfaces (#111451)
* docs(secrets): remove retired web credential paths

* refactor(web): remove retired provider compatibility paths

* refactor(providers): delete retired compatibility routes

* refactor(secrets): remove retired credential aliases

* refactor(plugin-sdk): delete retired compatibility surfaces

* docs(plugin-sdk): remove retired migration guidance

* chore(plugin-sdk): refresh rebased surface budgets

* chore(plugin-sdk): refresh API removal baseline

* refactor(compat): migrate retired internal callers

* chore(plugin-sdk): refresh current-main baselines

* test(config): migrate plugin-owned secret assertions

* test(gateway): narrow plugin secret refs

* fix(plugin-sdk): preserve private boundary type identity

* chore(compat): remove stale sweep references

* chore(lint): lower max-lines budget

* refactor(secrets): remove unused web helper

* build(plugin-sdk): drop removed compat entries

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): use Linux API baseline hash

* fix(plugin-sdk): preserve private bundled build entries

* fix(plugin-sdk): package private runtime facades

* fix(plugins): preserve external credential contracts
2026-07-19 11:04:48 -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
783a5d21cf refactor(config): purge numeric tuning knobs behind built-in defaults (#111382) 2026-07-19 07:35:45 -07:00
Peter Steinberger
06f5f73e47 refactor: own model discovery by runtime lifecycle (#111173)
* refactor(agents): prepare model runtime catalogs

Build lifecycle-owned model and auth snapshots, carry prepared stores into hot agent paths, and serialize config/auth publication.

Credits @zeroaltitude's #90741 investigation and benchmark approach.

* refactor: finish lifecycle-owned model discovery

* fix: align prepared model catalog contracts

* test: align lifecycle catalog mocks

* test: fix prepared catalog type fixtures

* refactor: split prepared model runtime ownership

* fix: import prepared runtime replacement gate type

* test: split media runtime coverage

* test: preserve image auth fixture key types

* test: isolate lifecycle gate fixtures

* chore: keep release changelog owned

* refactor: finish prepared model catalog migration

* test: keep catalog review fixtures scanner-safe

* refactor: preserve lifecycle model runtime ownership

* fix: close prepared runtime lifecycle races

* fix: preserve compaction workspace fallback

* chore: document btw generation rebinding

* fix: preserve prepared generation boundaries

* fix: keep model-list discovery flag explicit

* fix: serialize standalone model runtime activation

* refactor: migrate subagent model catalog lookup

* refactor: clarify doctor catalog lookup seam

* chore: refresh plugin sdk api baseline

* test: migrate swarm catalog dependency

* refactor(telegram): rename runtime catalog seam

* refactor: extract model-aware tool context

* test(models): isolate lifecycle catalog fixtures

* refactor(agents): avoid btw parameter rebinding

* fix(net-policy): align root ipaddr dependency

* fix(build): keep net policy dependency bundled

* fix(deadcode): document net policy compile dependency
2026-07-19 05:30:54 -07:00
Peter Steinberger
bbcfec9e96 fix(channels): prevent outbound echoes and expose native login (#111341)
* fix(channels): centralize outbound echo suppression

* chore(plugin-sdk): refresh channel outbound surface

* test(commands): avoid native plugin fallback loading

* refactor(channels): separate outbound echo state

* docs(changelog): note channel action decisions

* fix(discord): align webhook preflight call

* fix(discord): use type-only thread binding import

* fix(channels): stream login blocks and widen turn results

* test(channels): narrow drop-capable turn results

* fix(channels): address echo and reply edge cases
2026-07-19 03:49:11 -07:00
Peter Steinberger
58452de711 refactor(config): config-surface reduction tranche 1 — retire dead keys, dedupe channel schemas, add growth ratchet (#111142)
* refactor(config): retire dead and aliased config keys via doctor migrations

* refactor(config): dedupe bundled channel config schemas into shared builders

* feat(config): add config-surface count ratchet to doc-baseline check

* test(config): drop stale fixtures for retired config keys

* fix(doctor): migrate only positive finite MCP timeout aliases

* fix(migrate-hermes): emit canonical MCP timeouts only

* fix(config): satisfy lint and contract gates
2026-07-19 00:52:37 -07:00
Jason (Json)
9c7800467c feat(mcp): open App views from channel replies (#111211)
* feat(mcp): add portable channel app actions

* test(gateway): keep origin reset private

* fix(mcp): require a resolved reply channel
2026-07-19 01:44:15 -06:00
Peter Steinberger
938e3d5f20 refactor(channels): share durable ingress monitor (#111186)
* refactor(signal): share durable ingress monitor

* refactor(slack): share durable ingress monitor

* refactor(discord): share durable ingress monitor

* refactor(imessage): share durable ingress monitor

* refactor(twitch): share durable ingress monitor

* refactor(tlon): share durable ingress monitor
2026-07-18 21:33:09 -07:00
Peter Steinberger
333a3297af refactor(plugin-sdk): consolidate persistent dedupe claim loop (#110984) 2026-07-18 23:36:59 +01:00
Peter Steinberger
264a7b0d12 refactor(channels): consolidate manifest descriptors (#110985) 2026-07-18 23:18:28 +01:00
Peter Steinberger
d7de67ae02 feat: ask_user follow-ups — harness convergence, channel finalization + reactions, native cards, docked web panel (#110372)
* feat(ask-user): follow-up harness slice

* feat(ask-user): follow-up channels slice

* feat(ask-user): follow-up native slice

* feat(ui): dock question panel above composer with stepper and compact stream summaries

* docs: refresh follow-up integration maps

* test(ui): align terminal summary proof

* fix(infra): echo declared option answers in terminal status when free-text is allowed

* fix(infra): keep reaction answering when display labels are formatter-adjusted

* fix(agents): settle plain-text claims only after question registration commits

* fix(agents,apps): commit-ordered claim persistence, claim-aware prompt delivery, non-blocking question refresh

* fix(harness,infra): reaction-appropriate question copy, caller presentations honored

* fix(native,infra,agents): local-expiry eviction, value-addressed reactions, reserve-before-request

* fix(infra,android): dual-mode question resolver for compact callbacks; reset terminal retention on replayed pending

* fix(harness,discord): claim-aware prompt delivery in run helper; escape finalization labels

* fix(macos): merge transient-content visibility with question cards after main sync

* fix: repair ask user follow-up CI

* test: update limited bootstrap scope expectation

* fix: retain shared question card API

* chore: refresh native i18n inventory
2026-07-18 03:32:39 -04:00
Peter Steinberger
bee40b9e8e fix(messaging): align lifecycle follow-ups (#110413) 2026-07-18 06:14:24 +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
13c0e7492a fix(test): converge Slack harness state across module reloads (#110255)
* fix(test): converge Slack harness state across module reloads

Five reaction tests in monitor.tool-result.test.ts failed whenever a sibling
file's vi.resetModules() ran earlier in the same non-isolated worker: the
cached globalThis __slackClient kept routing reactions to the OLD test-helpers
module's mocks while tests asserted on the recreated module's fresh mocks —
reactions 'never fired' (replies survived via freshly injected runtime).
slackTestState is now a globalThis-backed singleton so every module
incarnation and the cached client share one state object. Also: reaction
assertions wait on the mock (reactions apply via detached debounce/queue
work), and stale Bolt handler registrations clear per test/stop so
waitForSlackEvent cannot match a previous provider's handler. Proven with six
consecutive full extensions/slack runs (was ~50% failure).

* style(slack): bracket global test-state access for underscore lint
2026-07-18 01:17:11 +01:00
Peter Steinberger
601ffa2530 refactor(channels): move inbound turn execution into core (#110095)
* refactor(channels): centralize turn execution lifecycle

* refactor(extensions): adopt core channel turn plans

* test(channels): ratchet modern dispatch ownership

* chore(channels): drop stale max-lines exemption

* fix(channels): allow async delivery error cleanup

* test(channels): await async delivery cleanup

* test(channels): await delivery error callbacks

* fix(channels): preserve delivery error contract

* fix(plugin-sdk): preserve channel inbound declarations

* test(channels): exercise core delivery plans

* fix(channels): await delivery error cleanup

* fix(channels): preserve async cleanup contract

* refactor(channels): trim dispatch boilerplate

* fix(channels): mask observe-only dispatch results

* refactor(channels): delete unused dispatch exports

* fix(msteams): preserve resolver config overrides

* refactor(channels): keep Teams turn config internal

* test(plugins): drop retired Teams runtime export guard
2026-07-18 00:55:46 +01:00
Peter Steinberger
5bab0c2ea8 feat(slack): support running as a real Slack user (user identity) (#109837)
* feat(slack): add user-identity config surface (session token/cookie)

Introduce an account-level identity discriminator ("bot" default | "user") for
the Slack channel, plus sessionToken/sessionCookie secret fields for the
browser-session (user) path. Schema enforces structural coupling only
(identity="user" rejects appToken/signingSecret/relay/non-default mode);
credential presence is deferred to runtime like bot/app tokens. Account entries
inherit the top-level identity. Adds env fallback (SLACK_SESSION_TOKEN/COOKIE,
default account only), secret-contract registration, provenance *Source fields,
and UI hints. Config surface only; resolver/transport are later phases.

* refactor(slack): identity-aware operation-token resolver + dedup

Route Slack read/write token selection through a single identity-aware
resolveSlackOperationToken: identity="user" selects the browser-session token
for both operations (companion cookie attached later at client construction),
identity="bot" keeps the exact userToken/botToken + userTokenReadOnly behavior.
Delete the duplicated token-selection gate in action-runtime and route direct
sends/DM opens through the shared resolver with identity-neutral error wording.
Behavior-neutral for bot identity; net non-test LOC -2.

* refactor(slack): retarget user identity to xoxp + companion app

Replace the abandoned browser-session (xoxc/xoxd) approach for identity="user"
with the official path: authenticate as the human via the existing userToken
(xoxp) while a companion app (appToken/socket or signingSecret/http) carries
events. Remove sessionToken/sessionCookie config, env vars, secret-contract
entries, resolver fields, and their tests. Invert the schema coupling so
identity="user" permits the companion-app transport credentials, and drop the
now-vestigial user-identity structural guard entirely (identity is a permissive
discriminator; credential presence is a runtime concern). Resolver returns the
user token for user-identity reads and writes. Behavior-neutral for bot
identity.

* feat(slack): user-identity provider transport (xoxp + companion app)

Wire the identity="user" event transport. The companion Slack app runs Socket
Mode (appToken) or HTTP (signingSecret) while Bolt authenticates as the human
via the user token (no bot token). Startup auth.test runs on the user token and
its user_id becomes the self/mention target and self-send dedupe id (Bolt's
lazy per-event authorize populates context.botUserId=user_id, so the existing
self-event middleware drops the human's own messages). Identity-aware token
requirements and diagnostics; the bot-token identity warning is suppressed for
user identity. Behavior-neutral for identity="bot".

* feat(slack): user-identity setup, doctor, and docs

Add the setup wizard, doctor/status, and documentation for identity="user".
The wizard collects a user token plus the companion-app transport credential
(app token for socket, signing secret for http) and only persists identity when
"user" so bot setups keep byte-identical generated config; doctor validates the
user token via auth.test and warns on a missing transport credential; account
inspection reports user-token status. Message actions are gated on the active
identity's credential so user accounts expose actions. Docs add the exact
companion-app recipe (user scopes, on-behalf-of-user message events, socket/http
examples, the DM/group-DM-only-via-user-scope note, and self-send dedupe).
Behavior-neutral for identity="bot".

* test(slack): isolate user-identity provider tests to fix parallel flake

The new user-identity provider tests shared the globalThis-stored Slack test
client/handlers across files under parallel isolate:false, so their reaction
closures could reference a prior file's state and intermittently fail sibling
monitor tests (monitor.tool-result reactions). Track started monitors and
abort/await them all in afterEach (even on failed assertions), reset the shared
auth.test/mock state, and dispose the global Slack test runtime per file via a
new disposeSlackTestRuntime helper. Full Slack suite green across repeated
parallel runs.

* fix(slack): regenerate channel config metadata after rebase

* fix(slack): resolve setup consistent-return and doctor dead-export lint

* style: format install script test

* docs(slack): regenerate docs map for user-identity section

* test(slack): use renamed createSlackBoltApp token param in interop test

The rebase folded main's new wrapReceiver test into the renamed
createSlackBoltApp signature (botToken -> token); update the call site.
2026-07-17 23:23:21 +01:00
Peter Steinberger
4f0171b188 fix(slack): stabilize tool-result reaction tests (#110105)
* test(slack): await tool-result reaction dispatch

* test(slack): rebind retained reaction mocks
2026-07-17 23:21:29 +01:00
Peter Steinberger
da44d52ac6 feat: ask_user — structured questions from the agent with web card, channel buttons, and text answers (#109922)
* feat(gateway): add transient question runtime (question.* methods + broadcasts)

* feat(agents): add blocking ask_user question tool with chat prompt delivery and text-reply claim

* feat(ui): interactive in-thread question cards for ask_user

* feat(channels): native tap-to-answer buttons for ask_user on Telegram, Discord, and Slack

* feat(ui): unify codex and gateway question cards with interactive gateway answering

* refactor(agents): collapse ask_user pending state to one registry; docs for ask_user

* fix(agents): include ask_user in normal gateway runs; add question-flow control-ui e2e

* test(ui): avoid credential-shaped fixture in question card test

* refactor(ui): reorder stream-group context keys

* fix(gateway,ui): validate question answers at resolve; reject secret/duplicate-label questions; UI retry and reconnect hardening

* fix(gateway,agents): canonicalize accepted option answers; bound ask_user option labels to 64 chars

* chore(ci): prune unused question exports, allowlist mobile question events, fix discord lint

* chore(ci): regenerate protocol/i18n/docs/tool-display artifacts for question surface

* fix(protocol): flatten QuestionRecord for native codegen; drop TS-only alias from schema registry

* chore(android): regenerate ask-user localization resources

* docs: regenerate docs map after rebase

* fix(ci): avoid stale read-only dependency disks

* test: remove stale reef lint suppression ratchet

* fix(ci): keep source locale drift advisory in release gates

* fix(ci): scope locale advisory handling to parity check
2026-07-17 22:24:17 +01:00
Peter Steinberger
433b3ce86d fix(line): await deferred ingress release on stop (#110104)
* fix(channels): await abandoned ingress claims

* fix(channels): preserve abandonment boundaries
2026-07-17 20:56:17 +01:00
Peter Steinberger
c45f299cea fix(slack): adopt durable ingress across Bolt and relay (#109910)
* fix(slack): adopt durable ingress drain across Bolt and relay with logical twin guard

Slack events now enqueue their raw envelope durably before the transport ack
on all three modes: the Bolt receiver wrap covers Socket Mode and HTTP
(event_id = Events API event_id), and relay frames enqueue before the router
ack (keyed by logical team:channel:ts since router delivery-id redelivery
stability is undocumented). Dispatch, retry, dead-letter, and 30d/20k
tombstones run through the core drain; claimed relay events retry until the
relay source reattaches after a restart. The in-memory + persistent inbound
delivery caches and the app-mention race machinery are deleted.

One dedupe layer deliberately survives: Slack emits both a message and an
app_mention event (distinct event_ids) for a single mention post, so a new
claim-based dispatch guard keyed on logical (team, channel, ts) collapses the
twins — commit at turn adoption, release on gated/abandoned dispatch so the
surviving twin can re-run the same gate. 24h/20k retention mirrors the
retired persistent cache.

Slack monitor test state now scopes a fresh OPENCLAW_STATE_DIR per test;
persisted guard keys otherwise dedupe unrelated fixture messages that reuse
ts values. Five reaction tests in monitor.tool-result.test.ts are flaky on
clean origin/main under full-suite load (verified 2-of-3 baseline runs);
tracked separately.

Autoreview: secret scanner false-positives on token-shaped test fixtures
(three files); full manual review performed instead, which caught and fixed
the twin-guard regression. Part of the #109657 fleet adoption program.

* fix(slack): remove unused ingress exports

* chore(slack): prune max-lines baseline

* docs(plugin-sdk): refresh API baseline hash

* test(slack): type dispatch mock argument

* test(slack): use managed temp root
2026-07-17 11:38:54 +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
a1dfd47edd refactor(sdk): collapse plan updates to typed steps (#109660)
* refactor(sdk): collapse plan updates to typed steps

Per owner decision, remove the shipped steps: string[] SDK field and its unreleased planSteps replacement early. Collapse onPlanUpdate to one typed steps field. Retain wire-level string normalization for external Codex plugin version skew.

* chore(sdk): tighten rebased surface budget
2026-07-16 22:35:29 -07:00
Peter Steinberger
96815a437b fix(channels): centralize access gates and media fallback (#109548)
* refactor(channels): shared supplemental sender gating, allowlist-match adoption, outbound mop-ups

* fix(plugin-sdk): skip-aware media sequence with text fallback for empty URLs

* chore(plugin-sdk): align surface budgets after rebase

* test(qqbot): type media sender mock calls

* fix(plugin-sdk): distinguish empty media sequences

* fix(plugin-sdk): track void media sends
2026-07-16 20:55:08 -07:00
iloveleon19
382d570cbf feat(channels): add unified implicit mention policy (#108829)
* feat(mattermost): add thread.requireExplicitMention to opt out of thread auto-follow

Mattermost treats any reply in a thread the bot has participated in as an
implicit mention, so requireMention only gates the first message and the bot
then answers follow-ups addressed to other people for the participation TTL.
Slack exposes channels.slack.thread.requireExplicitMention for exactly this;
Mattermost had no equivalent and its strict schema rejected the key.

Add channels.mattermost.thread.requireExplicitMention (channel + per-account),
mirroring Slack. When set, thread participation no longer counts as a mention.
Default (unset/false) keeps today's auto-follow behavior unchanged.

Related: #108269

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

* feat(channels): add implicit mention policy foundation

* feat(channels): unify implicit mention policy

Co-authored-by: leon <dodoma0919@gmail.com>

* refactor(channels): keep implicit policy inside evaluator

* fix(channels): use exported implicit mention type

* chore(channels): satisfy extension lint

* fix(config): break implicit mention type cycle

* fix(plugin-sdk): account for implicit mention config export

* refactor(config): isolate implicit mention schema

* chore(plugin-sdk): align implicit mention surface budget

* fix(config): remove unused schema re-export

---------

Co-authored-by: leon <dodoma0919@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 17:16:01 -07:00
Peter Steinberger
bd9a996b78 refactor(slack): drive progress drafts through the shared compositor (#109336)
* test(slack): drop rotating loading-message fixture expectations

* refactor(slack): drive progress drafts through the shared compositor

Slack's status_final progress path now uses the shared channel compositor
(preamble headline, reasoning, tools, plan checklist, commentary
arbitration) instead of its hand-rolled gate/merge/render pipeline, and
native task cards consume compositor snapshots with stable row identity
(line ids first, fixed reasoning id, occurrence-suffixed content hashes).
Finals in progress mode post as fresh messages that follow the draft's
thread; the working draft then collapses into a compact activity receipt,
while failed or error finals leave the draft as the turn record. The
rotating native loading messages are gone in favor of the plain typing
status. Queued and assistant-boundary turns re-arm with clean delivery
state, and native completion status survives transient append failures.
2026-07-16 14:50:36 -07:00
NIO
b2f11462c8 fix(slack): pass upload timeoutMs through guarded fetch dispatcher floors (#106496)
* fix(slack): pass upload timeoutMs through guarded fetch dispatcher floors

* test(slack): simplify upload timeout coverage

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 13:38:44 -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
66604df9b6 refactor(channels): share retry helpers and remove dead state probes (#108903)
* refactor(channels): generalize channel retry runner and retry-after parsing; drop dead configured-state probes

* fix(channels): align retry and metadata contracts
2026-07-16 04:03:15 -07:00
Hosan
7bc43f50b7 fix(message): reject invalid read targets without fallback (#108637)
* fix(message): fail closed on invalid read targets

* fix(message): scope read channel validation

* fix(message): fail closed on explicit read scope

---------

Co-authored-by: Hosan <200961079+hosanxiv@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:36:32 -07:00
zw-xysk
9154e0b123 fix(slack): bound userCache with channelCache-equivalent LRU prune (#105978)
Extract shared LRU map helpers so resolveUserName touches recency on hit
and pruneMapToMaxSize caps the map at 2048 after each successful users.info write.

Rebased onto current main; Evidence scrubbed of token suffixes.
2026-07-16 01:47:27 -07:00
Alix-007
2dff17c754 fix(slack): add timeout to startup auth.test requests (#105893)
* fix(slack): add timeout to startup auth.test requests

* test(slack): format auth startup timeout proof

* fix(slack): keep startup timeout test helper private

* fix(slack): keep auth timeout state lean

* test(slack): settle stalled auth test during cleanup

* fix(slack): keep startup timeout within LOC guard

* fix(slack): terminate stalled startup auth checks

* test(slack): align startup logger mock type
2026-07-15 23:20:20 -07:00
Peter Steinberger
bae9752c5a refactor(deadcode): enforce repository hard zero (#108641) 2026-07-15 22:40:00 -07:00
Peter Steinberger
513200125d feat(channels): render live plan checklists from typed update_plan snapshots (#108597)
* feat(channels): render live plan checklists from typed update_plan snapshots

Codex, Copilot, CLI, and embedded update_plan events now carry typed
{step, status} snapshots end to end instead of flattened strings. Slack,
Discord, Telegram, Matrix, and MS Teams render a live checklist in
progress drafts; Slack native task cards show the real plan with
position-keyed rows and id reconciliation. The embedded update_plan tool
is default-on for every model (tools.experimental.planTool: false opts
out). The shipped onPlanUpdate steps: string[] SDK field stays populated
during a deprecation window alongside the canonical planSteps.

* fix(auto-reply): align plan bridge types with SDK callback contract

* fix(auto-reply): split agent event bridge module and fix plan type boundaries

* fix(channels): satisfy oxlint on plan checklist helpers
2026-07-15 22:00:21 -07:00
Peter Steinberger
9317aadaa2 feat(slack): add opt-in scoped presence events (#108510)
* feat(slack): add opt-in presence events

* style(slack): satisfy presence lint rules

* chore: keep release changelog centralized

* fix(slack): satisfy presence integration gates

* fix(slack): preserve eligible presence targets
2026-07-15 17:43:26 -07:00
zw-xysk
30d5b74825 fix(slack): mark connected status degraded without bot identity (#105556)
* fix(slack): mark connected status degraded without bot identity

When auth.test fails or botToken is a user token (user_id without bot_id),
Socket Mode can still come up. Keep connected=true for transport liveness
but publish healthState=degraded with lastError instead of advertising healthy.

Rebased onto current main; credentials revoked by operator.

* fix(slack): report degraded socket identity health

Co-authored-by: zw-xysk <zhao.wang1@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-15 11:09:22 -07:00