Commit Graph

735 Commits

Author SHA1 Message Date
Peter Steinberger
8ef067a216 refactor(config): move Discord and Teams schemas to plugins (#112812)
* refactor(config): move Discord and Teams schemas to plugins

* refactor(discord): privatize config schema fragments

* style(discord): format private schema fragments
2026-07-22 21:02:12 -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
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
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
0adc2cf606 refactor(media): consolidate parallel media-kind unions onto canonical MediaKind (#112063)
* refactor(media): consolidate parallel media-kind unions onto canonical MediaKind

One canonical MediaKind union (media-core constants) replaces ~40
duplicate/parallel kind declarations across core and channel plugins;
channel-specific narrower contracts derive via Extract/Exclude. Also
fixes a review-caught fallback bug where a stored "unknown" reply-chain
kind preempted MIME inference and relabeled images as documents.

* refactor(ui): derive attachment kinds from MediaKind

* fix(telegram): drop type-dead unknown guard in reply-context kind fallback

* style(telegram): format media kind fallback
2026-07-20 22:27:22 -07:00
Peter Steinberger
6e19c36d41 feat(channels): batch 2 producers drop media placeholder bodies (#111665)
* feat(channels): batch 2 producers drop media placeholder bodies

Media-placeholder program batch 2: Discord, Feishu, and MSTeams stop
minting <media:kind> placeholder bodies. Primary bodies carry captions
only; native resources produce ordered structured facts with type-only
facts for failures/rejections (batch-1 alignment contract). Discord
stickers travel as native kind "sticker" (not MIME-derivable); text
carriers (forwarded snapshots, room history, thread starters, pending
text) render via formatMediaPlaceholderText. Feishu audio preflight and
MSTeams unavailable selection move from body-string equality to
structured facts; dead placeholder payload builders deleted.

* test(discord): make allowlist policy explicit
2026-07-19 22:48:32 -07:00
Peter Steinberger
c163daa4ed refactor(channels): share durable ingress monitor (#111249)
* refactor(mattermost): share ingress monitor

* refactor(nextcloud-talk): share ingress monitor

* refactor(msteams): share ingress monitor

* refactor(zalo): share ingress monitor

* refactor(sms): share ingress monitor

* refactor(line): share ingress monitor
2026-07-19 00:26:28 -07:00
Peter Steinberger
ba62b944fa fix(streams): preserve stable results when body cleanup fails (#111245)
* fix(proxy): preserve validation after cleanup failure

* fix(msteams): preserve attachment cleanup outcomes

* fix(gateway): preserve loopback size diagnostics
2026-07-18 23:25:20 -07:00
Peter Steinberger
8a5c8690e1 fix(secrets): keep unaffected owners live during reload failures (#110779)
* fix(secrets): isolate reload failures per owner

* refactor(secrets): split runtime activation helpers

* fix(secrets): export web warning type

* fix(secrets): reject unsafe degraded config writes

* fix(secrets): derive reload defaults type

* fix(secrets): defer reload state publication

* fix(secrets): preserve partial refresh state

* fix(secrets): retry superseded reload preflight

* fix(secrets): bind stale credentials to owner contracts

* fix(secrets): scope degraded credential contracts

* fix(secrets): restore source ownership guards

* fix(secrets): recover provider-only degradation

* fix(secrets): enforce degraded reload contracts

* fix(secrets): preserve scoped reload state

* fix(secrets): reconcile deferred descendant state

* fix(secrets): commit reload state atomically

* fix(secrets): preserve source transaction lineage

* test(secrets): use non-secret lineage marker

* chore(plugin-sdk): refresh API baseline

* fix(secrets): canonicalize web owner contracts

* fix(plugin-sdk): preserve legacy secret owner contracts

* fix(secrets): satisfy startup activation types

* test(secrets): align reload fixtures with owner contracts

* refactor(secrets): move source recovery scope helper

* fix(secrets): preserve owner contracts on web failures

* fix(secrets): bind legacy web resolution contract

* fix(secrets): retry stale auth publication
2026-07-18 18:29:24 +01:00
Peter Steinberger
4e0cc187a2 fix(msteams): preserve inbound activities before webhook ack (#110357)
* fix(msteams): adopt durable ingress drain with ack gated on activity append

Bot Framework webhooks acked before detached processing with no platform
retry, silently losing inbound activities on a crash. Dispatchable activities
(message + adaptiveCard/action invokes) now persist their raw JSON durably
(event_id = activity.id per the @microsoft/teams.api uniqueness contract,
lane = conversation.id) before the webhook 200; dispatch runs through the
core drain with deferred claims through debounce, merged-flush fan-out
adoption, gated-turn settlement, retry/dead-letter classification, and
30d/20k tombstones. Restart replay reconstructs proactive routing context.
No inbound replay guard existed; the outbound echo cache is untouched. Twin
check: messageUpdate/votes/consent/reactions run dedicated non-agent
workflows and are excluded from the journal.

Autoreview blocked by codex sandbox network in the build stage; full manual
review performed. Part of the #109657 fleet adoption program (wave 2).

* fix(msteams): close live-context races and double next() in drain dispatch

Three defects found in landing review: the live turn context could swap a
duplicate delivery's mutated activity in place of the journaled payload
(dispatch now always uses the journaled activity, live context is transport
surface only); the context registry installed entries after the durable
append, leaking them when the drain consumed the claim first (install now
precedes enqueue, tombstoned duplicates clean up, first delivery's context
wins); and a failed next() in the message handler was invoked a second time
from the catch fall-through (ran-flag guards it).

* fix(msteams): uninstall a failed append's live context before retry

Follow-up to the install-before-enqueue race fix: an enqueue rejection
bypassed cleanup, so a retry with the same activity id dispatched the failed
request's stale context. Uninstall is identity-guarded (only our context is
removed, never a concurrent redelivery's fresh install) and covers both the
throw and tombstoned-duplicate paths. Regression proves the retry's own
context dispatches.

* test(msteams): track the buffered dispatcher seam after main retired the settled variant

* test(msteams): resolve dispatch union before catch for promise lint

* test(msteams): explicit promise-returning mock for the promise-misuse lint

* test(msteams): type the ingress accept mock promise-returning

ReturnType<typeof vi.fn> types the implementation callback void-returning, so
every promise-returning accept mock tripped typescript(no-misused-promises)
in CI regardless of call-site shape.

* test(msteams): restore compact async mock now that its type permits promises

* test(msteams): extract gated-accept helper to stay under max-lines
2026-07-18 05:33:55 +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
wangmiao0668000666
dc0285366e fix(msteams): bound probe token acquisition to request deadline (#106386)
* fix(msteams): bound probe token acquisition to request deadline

probeMSTeams() at extensions/msteams/src/probe.ts:75 and :89 awaited
tokenProvider.getAccessToken(...) for the Bot Framework and Microsoft
Graph token endpoints with no surrounding deadline. The Microsoft
Teams SDK does not carry an inherent timeout on these calls, so a
stalled Azure AD token endpoint pinned the probe indefinitely.

Wrap both awaits with withMSTeamsRequestDeadline (default
MSTEAMS_REQUEST_TIMEOUT_MS = 30_000), matching the pattern already
used by six other MS Teams call sites: attachments/bot-framework.ts:252,
attachments/graph.ts:258, monitor-handler/message-handler.ts:594/654/685/692,
attachments/download.ts:167, team-identity.ts:37.

The probe was the one missing site. No new helper, no SDK change.
The existing outer catch at probe.ts:138 and inner catch at probe.ts:110
convert the timeout into a ProbeMSTeamsResult with ok: false and a
structured error field.

Added probe.timeout.test.ts: real probeMSTeams() with vi.mock
injected never-resolving getBotToken/getGraphToken; asserts the call
returns within the 30s bound instead of hanging to the proof budget.

* test(msteams): drive probe timeout test with vi.useFakeTimers

The original probe.timeout.test.ts waited 90 seconds of wall-clock per
focused run (3 stalled cases racing against a real setTimeout budget).
Per ClawSweeper P2 (automation), this material deterministic CI cost can
slow or time out test shards.

Drive the withTimeout race (from @openclaw/fs-safe/dist/timing.js, uses
setTimeout + clearTimeout) via vi.useFakeTimers() so each stalled case
resolves in milliseconds. Add one new case that spies on withTimeout's
timeoutMs argument to assert the production default deadline is exactly
MSTEAMS_REQUEST_TIMEOUT_MS = 30_000, so the production contract is not
silently weakened by the fake-timer change.

Per-case wall-clock: 25ms / 3ms / 2ms / 1ms / 2ms (was: 30s / 30s / 30s /
2ms / n/a).

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

* fix(msteams): bound remaining token acquisition

* test(msteams): keep credential fixture unchanged

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 01:21:49 +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
f341a013af test(release): repair plugin prerelease coverage 2026-07-17 13:43:12 +01:00
Monkey-wusky
9b238a7927 fix(msteams): cancel non-OK consent upload response body before throwing (#109701)
* fix(msteams): cancel non-OK consent upload response body before throwing

* fix(msteams): release all consent upload responses

Cancel unread response bodies after successful uploads as well as failed uploads, and fold the lifecycle assertions into the existing status-path tests.

Co-authored-by: Monkey-wusky <66244686+Monkey-wusky@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-17 01:18:40 -07: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
dabeae8c90 perf(test): tighten Teams lifecycle polling (#109686) 2026-07-16 22:52:25 -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
Peter Steinberger
83d82c9302 refactor(channels): persist runtime state in plugin SQLite (#109380)
* refactor(channels): move reef, msteams, matrix, zalouser file state to SQLite with doctor imports

* fix(channels): harden SQLite state migrations

* test(reef): use SQLite flow stores in receipt suites

* fix(reef): validate restored request policy

* fix(reef): retain replay state through relay window

* docs(reef): describe durable state import

* fix(reef): honor configured legacy state path

* chore: leave changelog to release flow

* fix(reef): use managed temp root in flow tests

* fix(matrix): omit undefined device id from state
2026-07-16 17:05:28 -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
990e8d24b0 feat(presentation): hosted-widget launch via typed web-app actions (#108927)
* feat(presentation): support hosted widget actions

* refactor(discord): route widget launches through presentation

* chore: keep changelog release-owned

* test(interactive): type the invalid web-app boundary case explicitly

* test: type web-app action fixtures against the split union

The two-member web-app union no longer accepts a distributed
type: "url" | "web-app" literal or an empty receipt in typed
callbacks; use explicit action casts and the Discord receipt factory.
2026-07-16 04:35:31 -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
Peter Steinberger
555c1a4e6a refactor(msteams): simplify request timeout cancellation (#108752) 2026-07-16 01:12:08 -07:00
Alix-007
292e8c3e72 fix(msteams): add timeouts to SharePoint file send requests (#104288)
* fix(msteams): add timeouts to SharePoint file send requests

* fix(msteams): clean up SharePoint timeout proof

* fix(msteams): use transfer deadline for SharePoint uploads

* test(msteams): normalize SharePoint abort rejection reasons

* fix(msteams): size SharePoint upload deadline by file length

* chore(deadcode): allowlist test-only timeout constant exports

* fix(msteams): harden SharePoint sharing deadlines

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 00:39:40 -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
894cc604a5 refactor(core): declare outbound target grammar on the messaging adapter (batch 2) (#107953)
* refactor(core): declare outbound target grammar on the messaging adapter (batch 2)

* test(outbound): drop a useless spread in the comparison fixture
2026-07-14 20:09:00 -07:00
Peter Steinberger
ed12937cbd refactor(plugin-sdk)!: delete the zero-consumer channel-ingress facade, AccessFacts projections, and dead dispatch aliases (#107906)
* refactor(plugin-sdk)!: delete the zero-consumer channel-ingress facade, AccessFacts projections, and dead dispatch aliases

* chore(plugin-sdk): refresh API baseline hash for the facade deletions

* fix(channels): drop dead AccessFacts field writes stranded by the facade deletion

* chore(plugin-sdk): refresh API baseline hash after rebase
2026-07-14 19:43:18 -07:00
Peter Steinberger
6d3eff97a0 refactor(channels): privatize Mattermost and MSTeams test seams (#107924)
* refactor(mattermost): privatize internal test seams

* refactor(msteams): privatize internal test seams

* docs(mattermost): fix public API import path

* refactor(deadcode): shrink export baseline
2026-07-14 19:11:46 -07:00
Peter Steinberger
5579f009fb refactor(channels): converge internal group-policy drift onto the scope tree (#107865)
* refactor(channels): converge internal group-policy drift onto the scope tree

* chore(plugin-sdk): refresh API baseline hash for scope-key exports

* fix(config): avoid shadowing the scopeKey export in the case-insensitive lookup
2026-07-14 18:30:04 -07:00
Alix-007
0c8220ebf9 fix(msteams): add timeouts to file consent upload requests (#104120)
* fix(msteams): add timeouts to file consent upload requests

* fix(msteams): share file consent upload timeout helper

* fix(msteams): size-budget file consent upload timeout

* chore(deadcode): allowlist test-only timeout constant exports

* style(msteams): format consent timeout test

* test(msteams): reject consent aborts with errors

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-14 10:14:23 -07:00
Peter Steinberger
f81f9d8570 chore: enforce max-lines suppression ratchet (#107315)
* ci: enforce max-lines suppression ratchet

* chore: prune stale max-lines suppression

* fix: close max-lines ratchet enforcement gaps

* fix: harden max-lines ratchet checks

* fix(ci): satisfy max-lines ratchet checks

* style: format max-lines declarations

* fix(ci): match oxlint suppression grammar

* test: isolate max-lines git fixtures

* chore: prune resolved max-lines debt

* test: skip newline path fixture on Windows

* fix: harden max-lines suppression ratchet

* chore: refresh max-lines baseline

* fix: close max-lines ratchet bypasses

* fix: derive ratchet base from PR merge tree

* fix: support older Git in staged ratchet

* fix: align max-lines declarations and baseline

* chore: refresh max-lines baseline for current main

* fix: exclude generated wizard locales from max-lines

* chore: prune resolved max-lines debt
2026-07-14 09:27:02 -07:00
Peter Steinberger
70dbbf3bcd fix(msteams): rename shadowed close callback in lifecycle test
ee472357cb added an app.listen callback param that the inner
server.close callback shadowed, failing oxlint no-shadow on main.
2026-07-14 03:17:59 -07:00
Peter Steinberger
ee472357cb refactor(msteams): use Express listen errors 2026-07-14 03:06:11 -07:00
Peter Steinberger
84500db379 refactor(plugin-sdk): isolate HTML entity runtime 2026-07-14 00:16:37 -07:00
Peter Steinberger
b58a75d726 refactor(html): share entity decoding 2026-07-14 00:00:05 -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
9c2dc92dd8 refactor(msteams): trim internal exports (#107148) 2026-07-13 22:01:56 -07:00
Peter Steinberger
8d465d71b5 fix(channels): progress drafts only for long tasks, with the model's own preamble as the status headline (#106026)
* fix(channels): time-gate progress drafts and defer narration until visible

Progress drafts started immediately on the second work event, so the 5s
initial delay never filtered small fast tasks and Discord showed narrated
status for turns that finish in seconds. The gate is now purely time-based
(explicit startNow paths like approvals/patches are unchanged), and the
progress narrator no longer spends utility-model completions before the
channel draft is visible - notes buffer so the first visible narration has
full context.

Surface: src/channels/streaming.ts gate, src/auto-reply/reply/progress-narrator.ts,
Discord draft-preview wiring; test updates across discord/matrix/msteams/slack.

Refs #105872

* feat(channels): model-preamble status headline for progress drafts

In progress mode the draft's status line is now the model's own typed pre-tool
preamble whenever it is fresh (always on, no new config); utility-model
narration fills the slot only when the model has been quiet for ~20s or emits
no preamble. Preambles never start the draft early - the time gate still owns
the small-vs-big decision - and narration generation is suppressed while a
fresh preamble exists, saving utility calls. Discord and Telegram render the
headline; the opt-in streaming.progress.commentary lane and its receipt
counters are unchanged.

Refs #105872

* refactor(channels): split status-text and telegram preview helpers to honor the LOC ratchet

The LOC ratchet forbids raising baseline entries, so the headline feature's
growth is paid for with clean splits: reasoning/commentary text normalization
moves to src/channels/progress-draft-status-text.ts, Telegram progress preview
rendering to extensions/telegram/src/progress-draft-preview.ts, and small
ownership moves (formatCompactionModelRef, resolveFollowupAbortSignal, shared
CLI preamble gate, Discord preamble item-event handling) shrink the runners
back under their pins. Baseline entries lowered to exact new counts; the
compositor entry is removed (424 lines, under the 500 ceiling).

Refs #105872

* test(auto-reply): add resolveFollowupAbortSignal to the queue mock factory

The explicit ./queue.js vi.mock factory must export every binding prod touches;
the relocated resolveFollowupAbortSignal was missing and failed the whole
followup-runner suite.

Refs #105872

* fix(channels): sanitize preamble headlines and retry skipped narration

Review findings: preamble headlines now strip inline delivery directives and
reject silent-control tokens (NO_REPLY) before rendering, sharing the
commentary sanitizer; rejected input keeps the previous valid headline. The
narrator schedules cancellation-aware retries when a run is skipped for draft
invisibility or preamble freshness, so single long tool calls still narrate
and the ~20s utility filler fires without needing another event. Silent
preambles no longer stamp freshness or add narration notes.

Refs #105872

* fix(channels): bound narrator visibility polling and gate Telegram headline mode on accepted preambles

Visibility retries stop after 30 one-second attempts (turn completion has no
narrator disposal signal) and re-arm on new activity; Telegram derives its
headline-rendering state from the compositor's sanitized headline instead of
raw payload text, so directive-only or NO_REPLY preambles keep structured
tool-line rendering. Docs scope the preamble headline to Discord and Telegram.

Refs #105872

* fix(telegram): read headline state from the compositor at render time

The progressHeadlineActive mirror flag was set after pushPreambleHeadline had
already rendered, so the first valid preamble arriving after the draft opened
rendered through the structured-line path and the headline never appeared
until the next event. The renderer and formatLine now read the compositor's
sanitized hasStatusHeadline directly and the mirror flag is deleted; adds an
ordering regression test. LOC baseline lowered to the exact new count.

Refs #105872

* fix(channels): let the opt-in commentary lane own preambles and scope narration docs to Discord

With streaming.progress.commentary enabled, the status headline would replace
the documented interleaved 💬 lines for any turn with a preamble (the exact
event the lane consumes). The compositor headline now declines when the
commentary lane is enabled, preserving that lane's shipped shape, and docs
state the utility-model filler is Discord-only today (Telegram's headline is
preamble-only; it has no narrator wiring).

Refs #105872

* fix(msteams): cancel the pending progress gate at finalize

With the purely time-based gate, every fast Teams progress turn leaves a
pending 5s start timer; the SDK resets its stream id on close, so a late
timer fired renderInformativeUpdate against the closed stream and posted a
stale working card below the final answer. finalize() now cancels the gate
and the informative renderer refuses to run once final text is queued.

Refs #105872

* docs(channels): record the headline-vs-commentary ownership decision inline

Refs #105872

* style: oxfmt the late review-fix rounds

Four files from the final review-fix commits were never oxfmt-formatted and
failed the shared CI formatting step; baseline lowered for the one-line
telegram shrink.

Refs #105872

* fix(channels): harden progress draft lifecycle

* fix(channels): refresh repeated preamble items

* fix(plugin-sdk): expose progress lifecycle options

* style(auto-reply): simplify narrator reset

* docs(channels): refresh progress draft docs map

* fix(auto-reply): clear queued narrator request context

* fix(discord): cancel delayed progress gate at final

* docs: refresh plugin sdk api baseline
2026-07-13 20:55:44 -07:00
Peter Steinberger
d9e360f047 refactor(msteams): use exact Teams SDK types 2026-07-13 12:54:19 -07:00
Peter Steinberger
88e0bbc86f fix(ci): drop unused msteams media placeholder test constants
Companion to 4bb4ffc55c: check-lint (no-unused-vars) still fails on
attachments.helpers.test.ts after #106279 trimmed their usages.
2026-07-13 03:43:17 -07:00
Peter Steinberger
4bb4ffc55c fix(ci): drop dangling msteams attachment placeholder helper 2026-07-13 03:42:39 -07:00
Peter Steinberger
1577c28500 refactor(deadcode): trim msteams private exports (#106279)
* refactor(deadcode): trim msteams private exports

* chore(deadcode): refresh unused export baseline
2026-07-13 03:26:24 -07:00
Peter Steinberger
9b565e8d50 refactor(plugins): trim long-tail dead exports (#106230)
* refactor(extensions): trim long-tail plugin exports

* refactor(whatsapp): remove dead media relay
2026-07-13 02:34:06 -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
774d5249cc test: eliminate unchecked TypeScript casts (#105804) 2026-07-12 18:08:26 -07:00
Vincent Koc
6c88469110 refactor(msteams): remove obsolete sso invoke stack (#105799) 2026-07-13 08:59:08 +08:00
Peter Steinberger
8573f9e016 test(msteams): align stream failure trace (#105509) 2026-07-12 17:19:03 +01:00
Galin Iliev
787ed383f5 fix: Microsoft Teams streaming keeps chunk after whitespace collapse (#102357)
* fix(msteams): preserve streaming delta after whitespace collapse

* fix(msteams): clear stale stream text on rewrite fallback

* test(msteams): update delivery trace stream mock

---------

Co-authored-by: Peter Steinberger <steipete@openai.com>
2026-07-12 08:01:19 -07:00