Commit Graph

1043 Commits

Author SHA1 Message Date
Peter Steinberger 07c8b42a71 fix(channels): preserve delivery after preview cleanup (#126922) 2026-08-20 18:18:25 -07:00
Peter Steinberger 319df0e7a1 test: trim plugin test duplicates (#126917) 2026-08-20 18:07:27 -07:00
Peter Steinberger a4178c7eb1 fix(discord): demote expected command truncation logs (#126824) 2026-08-20 13:26:44 -07:00
Peter Steinberger aafa61be65 fix(discord): preserve message text from every embed (#126752)
* fix(discord): preserve text from every message embed

* chore(discord): shrink assertion baseline after embed cleanup
2026-08-20 10:28:20 -07:00
Peter Steinberger 2fe6c0206d fix(discord): retain attachment-only reply context (#126204) 2026-08-19 15:55:55 -07:00
Peter Steinberger 554dfbe0a2 feat(discord): auto-join occupied voice rooms (#125974)
* feat(discord): auto-join occupied voice rooms

Add opt-in voice.autoJoin[].whenOccupied residency so Discord voice bots join for the first human and leave when the room becomes empty while preserving existing always-on, manual, transcript, and follow-user behavior.\n\nCloses #125973

* test(discord): isolate process runtime mocks

Use stable hoisted runtime-env mocks so isolate=false Discord test ordering cannot turn sleepWithAbort back into an unmocked function.

* fix(discord): defer unknown voice occupancy

Treat memberless voice states as unresolved instead of human so bot-only rooms cannot trigger occupancy-managed auto-join. Add cache-to-listener and manager regressions.\n\nCloses #125973

* test: isolate shared module mocks

Replace ineffective non-isolated module spies with stable hoisted mocks and a child-process SQLite connection-reuse probe so gateway and Discord shards are order-independent.

* test(gateway): inline connection reuse probe

Keep the child-process SQLite ownership probe in its owning Vitest file so Knip sees the full test surface without weakening process isolation or the original order regression.
2026-08-19 00:08:54 -07:00
Peter Steinberger baefd067bb fix(deps): keep package runtime dependencies single-owned (#126119)
* fix(deps): consolidate shared runtime helpers

* test(concurrency): support current test lib target

* fix(time): preserve year-scale plugin durations

* fix(agents): preserve empty subagent completions
2026-08-18 22:12:45 -07:00
Peter Steinberger cc273e6eea fix(channels): stop idle ingress retention writes (#126073)
* fix(channels): prune ingress retention on admission

* style(channels): keep ingress monitor within limit

* fix(channels): skip pruning ignored ingress
2026-08-18 19:05:30 -07:00
Peter Steinberger 2bed8caf9e feat(ui): channel conversation avatars in the sidebar (Discord + Slack) (#125668)
* feat(gateway): proxy channel conversation avatars

* feat(discord): capture conversation avatars

* feat(slack): capture DM sender avatars

* test(discord): bind guild avatar mock

* feat(ui): render channel conversation avatars

* fix(ui): align sidebar owner fixtures

* fix(gateway): version channel-avatar routes by media revision

A stable per-session URL let AuthenticatedAvatarRouteLoader's blob and
sticky-404 caches pin a mounted row to a stale or blank avatar after the
backing media changed. Append an opaque digest of the media reference so
replacement and 404-recovery change the route identity.

* test(ui): align sidebar owner facet

* fix(ui): keep owner chip until channel avatar loads

A session with a channelAvatarUrl suppressed its owner chip even while the
blob was loading, auth was not ready, or the route 404ed, leaving an empty
lead slot. The chip now rides as fallback content inside the avatar element
and yields only to a usable image. Covers 404 and auth-pending states;
avatar rows keep renderedOwnerId unset so an owner-viewer stays visible in
the facepile.

* perf(ui): keep channel avatar fallback within budget

* perf(ui): lazy-load the channel avatar element

The avatar element and its authenticated blob loader rode the startup
bundle through session-leading-indicator, pushing startup JS 51 B over the
CI gzip budget. Channel avatars are not startup-critical: register the
element on the first avatar row; the owner-chip fallback covers the
one-time upgrade window. Startup JS returns ~1 KiB under the ceiling.

* build(ui): raise startup baseline for channel avatars

CI-measured startup JS is 344379 B against a 343289 B baseline (+1090 B).
The avatar element and blob loader are code-split out of startup (previous
commit); the residual is the sidebar lead-slot render branch and row
plumbing, which cannot be deferred. Baseline updated via
check-control-ui-performance --update-baseline with CI bytes per the
script's contract; well inside the 4096 B ratchet step and 358400 B
ceiling.
2026-08-18 09:55:50 -07:00
Peter Steinberger eb38d5e486 fix(reply): report settled delivery outcomes once (#124773)
* refactor(reply): publish settled dispatch receipts

* refactor(reply): isolate dispatch outcome accounting

* refactor(discord): infer settled dispatch result

* test(reply): model settled dispatch receipts

* test(reply): type settled receipt fixtures

* test(telegram): declare settled final receipt

* test(telegram): adapt legacy dispatch fixtures

* fix(reply): complete settled receipt compatibility

* test(channels): keep delivery suite within lint budget

* test(channels): reuse settled count fixture

* test(channels): consolidate receipt assertions

* test(channels): extract delivery receipt fixtures

* fix(qa): recover stopped clients after config restart

* fix(reply): settle deferred delivery receipts

* test(channels): share delivery turn fixtures

* fix(channels): preserve non-visible reconciliation

* fix(reply): preserve settled receipt compatibility

* refactor(reply): make settled receipts canonical

* refactor(reply): absorb settlement at dispatcher boundary

* test(matrix): prove settled receipt precedence

* refactor(sessions): extract prompt snapshot types

* fix(reply): restore queued admission counts
2026-08-17 06:38:16 -07:00
Peter Steinberger 185b1ab726 fix(discord): retain progress drafts after error finals (#125140)
* fix(discord): retain error progress drafts

* test(discord): parse REST probe request URLs

* test(qa): force Discord error-final path

* test(qa): match Discord overload final
2026-08-17 02:22:02 -07:00
Peter Steinberger 5ebfbbf8d7 fix(plugins): honor per-agent runtime config (#124978)
* fix(plugins): resolve per-agent config through SDK

* test(codex): preserve agent runtime exports

* test(telegram): make default owner explicit

* refactor(plugins): use lightweight agent scope runtime

* fix(codex): preserve multi-agent execution ownership

* chore(plugin-sdk): record approved agent scope exports

* fix(codex): keep scoped sandbox ownership authoritative

* fix(codex): preserve agent scope in native side actions

* fix(ci): avoid counting node check as environment variable
2026-08-17 01:53:08 -07:00
Peter Steinberger 7349177ce3 feat: main-session agent-wide visibility + session.groupScope routing (#124965)
* feat: add main session group routing

* docs: explain main session routing scopes

* fix: align memory session visibility

* test(qa): cover main-scoped group bindings

* fix(sessions): preserve binding-scoped outbound routes

* fix(routing): preserve explicit outbound owners

* fix(sessions): recognize global main visibility

* chore(ci): prune assertion safety baseline
2026-08-16 19:57:18 -07:00
Peter Steinberger 2012af16f2 refactor(channels): delete the per-turn progress receipt line (#124972)
The finished-turn digest ('🧠 N thoughts · 💬 N notes · 🛠️ N tool calls ·
⏱️ Ns') was synthesized onto three surfaces: Slack's Block Kit card footer
(removed in #123851), Discord's final-answer subtext, and a duplicate Telegram
implementation added to match Discord.

Nothing gated it — no config key for the receipt has ever existed on any
channel — so PR #122976, which deleted it from Slack's native progress surface,
could only ever be a partial removal. Finish it: live progress surfaces keep
their in-flight indicators, and a finished turn leaves the answer with nothing
synthesized appended.

Telegram's window collapsed *into* the digest, so it now retires like every
other surface; Discord already discarded its draft after the final landed. The
core tracker keeps only the live counters Slack's working footer reads.
2026-08-16 19:14:42 -07:00
Peter Steinberger 080887df92 refactor(types): discriminated unions for internal contracts — pilot lane A (#124845) 2026-08-16 14:49:28 -07:00
Peter Steinberger 541f373bc0 refactor(validation): type producers and drop internal re-validation — pilot lane C (#124808) 2026-08-16 13:38:17 -07:00
Peter Steinberger 568b920b21 feat(lint): enforce import ordering and deduplication (#124730)
* refactor(imports): dedupe and hoist imports

* feat(lint): enforce import/no-duplicates and import/first
2026-08-16 11:44:52 -07:00
Peter Steinberger acbbff19ba refactor(types): drain chained-assertion ledger — extensions (#124378)
* refactor(types): drain chained-assertion ledger — extensions

* fix(types): parse Matrix summary events at the boundary

* style(types): satisfy extension map lint

* style(types): name the Tlon image marker cleanly

* fix(browser): preserve deep MCP snapshots iteratively
2026-08-15 20:55:01 -07:00
Peter Steinberger f69226b7b4 test(discord): remove thread cache reset (#124278) 2026-08-15 13:59:03 -07:00
Pavan Kumar Gondhi 8668aeb969 fix(discord): bind transcript capture to source account [AI] (#118579)
* fix(discord): bind transcript capture to source account

* style(agents): keep transcript tool wiring compact

* fix(transcripts): declare account binding channels

* fix(transcripts): report effective capture account

* fix(transcripts): enforce account lifecycle ownership

* fix(transcripts): preserve cross-surface control

* fix(copilot): preserve transcript channel context

* fix(transcripts): fail closed for legacy channel owners

* fix(transcripts): add trusted legacy recovery

* fix(transcripts): preserve auto-start cleanup ownership

* fix(transcripts): reject untrusted account starts

* fix(transcripts): keep persisted ownership authoritative

* fix(transcripts): harden legacy recovery

* fix(transcripts): preserve agent ownership boundary

* fix(transcripts): scope account binding to source channel

* fix(transcripts): preserve unattributed owner isolation

* fix(transcripts): own configured captures by account

* docs(plugins): clarify transcript auto-start ownership

* test(transcripts): cover account-less recovery

* docs(transcripts): scope legacy recovery by provider

* fix(discord): reuse eligible account ordering for transcripts

* test(discord): use neutral transcript account fixtures

* fix(transcripts): keep accountless recovery local

* fix(discord): resolve transcript accounts by voice capability

* fix(transcripts): bound account resolution failures

* fix(transcripts): bound account tool output

* fix(transcripts): honor unresolved provider accounts

* fix(transcripts): preserve binding when providers are missing

* fix(transcripts): fail closed on unknown binding provenance

* fix(transcripts): qualify account lifecycle capability

* fix(transcripts): normalize provable legacy owners

* fix(transcripts): bind scheduled capture to caller authority

* fix(transcripts): preserve scheduled caller identity tuple

* fix(transcripts): preserve channel-less scheduled authority

* fix(plugin-sdk): publish transcript provider types

* fix(transcripts): use exact lifecycle ownership tokens

* fix(transcripts): preserve local ownerless lifecycle access

* fix(transcripts): allow local configured capture control

* fix(transcripts): preserve scheduled caller channel

* fix(transcripts): retain named-agent legacy recovery

* fix(transcripts): deny unrelated remote channels

* fix(doctor): validate transcript owner inference

* fix(transcripts): restrict legacy remote recovery

* fix(ci): align transcript Doctor checks

* fix(transcripts): require Doctor-owned legacy metadata

* fix(transcripts): reject unowned remote capture starts

* fix(transcripts): reject unbound Discord lifecycle calls

* fix(transcripts): distinguish legacy owner rows

* test(discord): keep unavailable account fixture typed

* fix(transcripts): mark current imports for Doctor

* fix(transcripts): complete account ownership validation

* fix(discord): restore transcript package boundary

* fix(discord): preserve bundled transcript entry boundary

* docs(transcripts): clarify Discord auto-start account

* fix(transcripts): bind account-owned imports

* fix: preserve transcript and cron policy state

* fix(cron): preserve scheduled transcript authority

* fix(discord): keep legacy transcript rows local

* fix(transcripts): narrow account ownership boundary

* fix(transcripts): preserve trusted caller ownership

* fix(discord): enforce transcript source authorization

* fix(ci): bound Control UI gzip build variance

* test(qa): align transcript scenario contracts

* fix(agents): repair rebased caller context

* fix(discord): restore rebased account ownership

* test(discord): restore voice account fixtures

---------

Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
2026-08-15 12:10:43 -07:00
Vyctor H. Brzezowski 82efd1f0ee fix: generate session and thread titles in sentence case (#123389)
Generated titles came back in Title Case because none of the title prompts stated a capitalization style; asked for a "title" with no case rule, models default to Title Case and nothing downstream corrects it. The tool-call purpose titles already ask for sentence case, so session titles, Discord auto-thread titles, and Telegram DM topic labels were the outliers.

Sets sentence case in each title prompt rather than post-processing: the titles follow the user's language, and sentence-case rules are language-specific, so a mechanical normalizer would mangle German nouns and proper nouns like OpenClaw or GitHub.
2026-08-15 01:38:41 -03:00
TUARAN d30c036b68 fix(channels): bound errored ingress settlements (#120104)
Route error-bearing fan-in abandonment through the existing failure policy so deterministic pre-adoption failures cannot exceed retry limits and starve same-lane messages.

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-14 21:17:17 -07:00
Dallin Romney 83d53044a4 fix(channels): route commentary through one progress owner (#121009)
* fix(slack): keep commentary progress on draft lane

* fix(channels): route commentary through one progress owner

Keep non-verbose commentary in Slack and Discord draft lanes while preserving one durable commentary payload when verbose progress makes those drafts yield. Freeze that owner decision for the turn so session changes apply on the next turn.

Co-authored-by: Dallin Romney <dallinromney@gmail.com>

Punchcard-Session: amber-workshop-workshop-36

* fix(channels): refresh queued commentary owner

Recompute the frozen draft-versus-durable commentary owner for every queued follow-up turn and carry that decision into final payload projection.

Punchcard-Session: amber-workshop-workshop-36

* fix(channels): clarify commentary owner opt-in

* fix(channels): preserve queued draft preambles

* fix(channels): require explicit queued progress ownership

* refactor(channels): record commentary progress owner

* fix(telegram): tolerate unscoped queued updates

* test(discord): use canonical draft fixture after rebase

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-08-14 22:15:53 +08: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 902eddf16b fix: keep global channel events agent-scoped (#123316)
Slack and Discord routed system events now retain route ownership when they enter literal-global queues, so sibling agents cannot consume them. Genuinely unbound routing remains an explicit error instead of falling back to a default agent.
2026-08-13 14:45:19 -07:00
Peter Steinberger 249a38d29f fix: keep background work scoped to its selected agent (#123257)
* fix: preserve explicit agent ownership

Scoped route, session, and job owners now survive hooks, cron, process exits, global delivery, bindings, and media cleanup. Truly ownerless operations remain explicit errors.

* test: provide session memory hook owner

* test(ui): close failed-module page explicitly

* test(matrix): align ownership and replacement proof
2026-08-13 11:29:31 -07:00
Peter Steinberger c218187244 fix(ci): stop killing healthy discord Vitest runs and repair partial runtime-env mocks (#123131)
The discord extension shard's silent transform/import startup (~210s measured
on a loaded macOS host) exceeded the run-vitest wrapper's 120s default
no-output timeout, so the watchdog killed healthy runs (#123025). The config
now gets the extra-long budget like the other large shards.

Three discord tests mocked openclaw/plugin-sdk/runtime-env with bare factories
missing most exports; under isolate=false they poison the shared worker module
cache and break later files that bind logVerbose/sleepWithAbort through the
shared process test harness. They now spread importOriginal and override only
their stubs.

Fixes #123025
2026-08-13 03:54:22 -07:00
Peter Steinberger 792b2054df refactor(agents): move recovery tests to owners (#123022)
* test(agents): move recovery tests to owners

* test(discord): split send coverage by surface

* test(discord): share send mock lifecycle

* test(discord): isolate draft timer coverage
2026-08-13 01:29:05 -07:00
joshavant be7a2c23e9 fix(discord): cancel started ingress jobs on abort 2026-08-13 01:34:05 -05:00
joshavant 0bea408976 fix(discord): cancel skipped queued ingress 2026-08-13 01:34:05 -05:00
joshavant 59c382b717 fix(tests): respect plugin helper boundary 2026-08-13 01:34:05 -05:00
joshavant 13750feff3 fix(plugin-sdk): preserve cancellation receivers 2026-08-13 01:34:05 -05:00
joshavant b0a8cdb6f7 fix(discord): settle mixed cancellation fan-in 2026-08-13 01:34:05 -05:00
joshavant 5af3d49621 fix(plugin-sdk): make ingress cancellation fan-in safe 2026-08-13 01:34:05 -05:00
joshavant 06600e2ca0 fix(discord): unblock ingress after retry exhaustion 2026-08-13 01:34:05 -05:00
Peter Steinberger 52cda537a4 test(discord): move ready abort proof to lifecycle (#122869) 2026-08-12 16:54:20 -07:00
Peter Steinberger 144a598831 refactor(discord): remove thread binding test API (#122744) 2026-08-12 10:40:53 -07:00
Peter Steinberger 08142099da refactor(plugins): remove test-only facades and adapter (#122532)
* refactor(plugins): trim test-only facades

* refactor(whatsapp): remove legacy outbound adapter
2026-08-12 01:20:09 -07:00
Peter Steinberger b080dd1e76 refactor: consolidate coercion contracts (#122458)
* refactor: consolidate coercion contracts

Centralize exact string, record, numeric, date, Boolean, argument, and structured-error coercions while preserving call-site semantics.

Migrate canonical-name collisions and deprecated internal SDK bypasses, deleting 55 net production/tooling lines. Expand declaration ownership enforcement to 101 allowed helpers and add a narrow export-completeness audit.

* fix: preserve standalone script coercions

Keep copied Control UI tooling self-contained and retain the trusted release harness module-relative source seam when the harness runs against an old target cwd.
2026-08-11 23:26:37 -07:00
Peter Steinberger 90beb639e7 refactor(discord): split voice lifecycle ownership (#122479)
* refactor(discord): split voice subsystem by lifecycle owner

* fix(discord): make one generation the voice lifecycle authority

* refactor(discord): delete superseded voice plumbing

* test(discord): split voice coverage by lifecycle owner

* chore(lint): ratchet max-lines baseline after voice split

* fix(discord): re-read voice lifecycle after realtime connect

* test(discord): move wake follow-up coverage to turns owner

* refactor(discord): keep realtime lifecycle state private

* refactor(discord): keep voice test harness type private

* refactor(discord): remove split voice test accessors

* test(discord): type split voice fixtures at owners

* test(discord): trim split voice test imports
2026-08-11 23:23:26 -07:00
Ayaan Zaidi dbb9665e7e fix(discord): let active turns receive steering (#122452)
Remove Discord's duplicate whole-turn session serialization so corrections reach shared reply admission and steer the active run. Preserve shared steer, followup, collect, and interrupt behavior.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-12 06:18:55 +00:00
Ayaan Zaidi ec4ae78b75 fix(agents): generate session titles with runtime-owned auth (#122163)
Use the selected harness for isolated title generation, including native Codex auth.
Retry failed dashboard titles from the first user message without overriding manual names.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-12 09:42:16 +05:30
Peter Steinberger 964c8c84c1 refactor: consolidate coercion ownership (#122299)
* refactor: consolidate coercion ownership

Centralize four canonical coercion helpers, migrate exact core and plugin duplicates through narrow Plugin SDK facades, and enforce declaration and plugin-normalization ownership boundaries.

The sweep adds eight focused SDK exports while deleting more production and tooling code than it adds. User-visible behavior is unchanged except for safer equivalent object and UI parsing at existing boundaries.

* fix: guard integer option ownership

Register resolveIntegerOption with the canonical function owner and extend the declaration-guard fixture so future local duplicates fail validation.

* fix: keep integer helpers on numeric facade

Remove the unshipped duplicate string-coerce exports and route every affected plugin consumer through the existing number-runtime contract.

* fix: point numeric coercion to number runtime

Make boundary and declaration diagnostics recommend the canonical numeric facade, with failing-before coverage for both guidance paths.
2026-08-11 17:14:53 -07:00
Peter Steinberger b350f76484 fix(channels): preserve failed agent run reactions (#122009) 2026-08-11 15:26:44 -07:00
Peter Steinberger 86bc5aa726 test(plugins): remove stale test plumbing (#122175) 2026-08-11 11:01:05 -07:00
Peter Steinberger cad77fb39c refactor: consolidate remaining coercion helpers (#122020) 2026-08-11 10:22:01 -07:00
Ayaan Zaidi 210aca6de3 fix(gateway): prevent restart replay after final delivery (#121908)
Re-lands the reverted #121507 on the current custody contracts. The reply dispatcher now owns direct-send pending-final custody: claim before provider I/O, terminal settlement for delivered/suppressed/failed outcomes, proven no-send stays replayable, ambiguous evidence fails closed — so Gateway restarts can no longer duplicate an already-accepted final reply.

Proof: ClawSweeper local review clean, exact-head ci-gate green, live Telegram E2E (one turn, one final, no duplicates).

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-11 13:09:59 +05:30
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 d6f70a96cb fix(plugins): native commands execute the selected plugin (#121544)
* fix(plugins): preserve selected command identity

* test(telegram): use scoped command registries

* test(telegram): isolate command runtime fixtures

* test(telegram): warm native command runtime

* refactor(plugins): keep command metadata private

* fix(plugins): accept synchronous command handlers

* fix(plugins): scope command drain bypass to live execution

* test(telegram): use scoped command registry fixtures

* test(telegram): isolate native menu runtime fixtures

* test(telegram): isolate login session store

* test(telegram): surface login flow failures

* test(telegram): preload native login module

* test(telegram): scope native command registries

* fix(plugins): complete command dispatch contracts

* fix(plugins): break command dispatch import cycles

* fix(plugins): stabilize command dispatch contracts

* fix(channels): keep plugin dispatch options internal

* fix(plugins): keep command dispatch carrier opaque

* test(channels): align delivery adapter fixtures

* test(delivery): align custody ownership coverage

* test(delivery): align latest queue reconciliation

* test(channels): drop obsolete delivery wrappers

* fix(plugins): rebind channel reload starts

* fix(plugins): scope command catalog reloads

* fix(ci): align current runtime contracts

* chore(plugin-sdk): refresh API baseline
2026-08-10 19:30:47 -07:00
Ayaan Zaidi 9935ca3b30 fix(approvals): bind native requests to channel accounts (#121673)
Native approval delivery and resolution now stay bound to the originating or explicitly targeted channel account. Unbound requests fail closed across multiple eligible accounts; trusted reviewer-less SDK callers remain compatible.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-11 01:42:41 +05:30