Commit Graph

427 Commits

Author SHA1 Message Date
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 cad77fb39c refactor: consolidate remaining coercion helpers (#122020) 2026-08-11 10:22:01 -07:00
Ayaan Zaidi 2c8ed54ddb feat(heartbeat): default delivery to the configured owner, never groups (#121988)
Unset heartbeat.target now resolves "owner": elected heartbeat notifications deliver to the operator's DM resolved from commands.ownerAllowFrom or the channel allowFrom (first concrete entry; wildcards and channel-scoped wildcards excluded; configured owners exhausted across channels before any channel-local fallback). Delivery requires the channel's own classifier to positively prove a direct destination — every bundled messaging plugin now ships an inferTargetChatType contract — and unproven or group-shaped destinations fail closed to the visible no-route state. The first implicitly-routed delivery carries a one-line self-explanation naming the target: "none" opt-out. Explicit target "last" remains as the follow-the-conversation opt-in. Refines the unreleased #121892 default before it ships; refs #121880.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-11 13:18:22 +00:00
Ayaan Zaidi 73d4c07bd5 fix(delivery): record ambiguous final loss as durable notice debt (#121833)
A final reply whose platform send was accepted but whose response was lost
previously ended in silence. Custody that stays unknown after a claimed send
now records durable pendingDeliveryNotice debt; the next same-route turn
delivers one "could not confirm delivery" notice and acknowledges it into the
transcript. Restart recovery completes ambiguous sessions with the same debt
instead of a fire-and-forget notice; the debt survives reset and rollover, and
suppressed notice sends retain it instead of faking delivery. Permanent typed
no-send rejections settle as terminal suppression (no replay, no false
notice); retryable ones restore prepared custody for safe replay. Google Chat
media-only rejections use the typed no-send contract; Telegram native-command
replies join pending-final custody.

Fixes #80362

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-11 09:25:04 +00: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
Peter Steinberger 8616c0c374 refactor: finish shared test helper migrations (#120996)
* test: finish shared helper migrations

* test: fix helper migration CI

* style: fix test import ordering

* test(acpx): restore deferred void types

* test: fix helper migrations after rebase
2026-08-09 06:00:06 -07:00
Peter Steinberger e1ec95dcbf refactor(auth): dedupe profile upserts and approval resolvers (#120831)
* refactor(auth): dedupe profile upserts and approval resolvers

* test(auth): mock canonical locked upsert

* test(auth): mock locked upsert during onboarding
2026-08-08 21:14:16 -07:00
sunlit-deng 6287c88c3b fix(googlechat): reject invalid UTF-8 in API JSON responses (#120239)
… regression

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-08 20:54:17 -05:00
Peter Steinberger 8cb53c7b55 perf(doctor): keep bundled doctor contract closures dependency-light (#120698)
* perf(doctor): keep bundled doctor contract closures dependency-light

Doctor contract enumeration cold-loads each plugin's doctor-contract-api
closure via jiti, so a static value import of openclaw/plugin-sdk/runtime-doctor
pulled the state-db/kysely graph (~4.3s per closure) into
listPluginDoctorLegacyConfigRules / listPluginDoctorStateMigrationEntries.

- migrate all light doctor-contract closures (66 files) to the
  dependency-light openclaw/plugin-sdk/runtime-doctor-migrations subpath
- voice-call: load detect/repairOpenClawStateDatabaseSchema* lazily inside
  the migration bodies; keep only a type-only static runtime-doctor import
- matrix: split pure credential record shapes/normalizers into
  credentials-state.ts so the doctor closure no longer imports the sync
  plugin-state store through credentials-read
- guard: doctor-contract-closure-guard.test.ts now forbids static value
  imports of runtime-doctor in closures alongside agent-runtime

* fix(matrix): keep credential revocation record type module-local

Knip production scan flags the export as consumer-less; the type is only
referenced by the exported union and revocation guard signature.
2026-08-08 17:51:31 -07:00
Leon-SK668 d64f5b6a09 fix(googlechat): redact reflected credentials in API errors (#119965)
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-06 18:21:13 -05:00
Peter Steinberger 3d65ea2a1b fix(googlechat): align doctor policy ownership with canonical account configuration (#118972)
* fix(googlechat): align doctor policy ownership with canonical account configuration

* test(googlechat): align generated channel metadata and typed doctor fixtures
2026-08-03 15:23:16 -07:00
Peter Steinberger 554968fba6 fix(googlechat): recover durable lanes after oversized attachments (#118986) 2026-08-03 15:12:17 -07:00
Peter Steinberger 386228ad66 fix(googlechat): block webhook startup without valid audience configuration (#118968) 2026-08-03 14:31:34 -07:00
Peter Steinberger f9d9d1225a refactor(channels): own the lifecycle status contract in SDK patch factories (#118795)
* refactor(sdk): add channel lifecycle patch factories

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

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

* refactor(runtime): lifecycle-own ambient registries

* test(slack): assert lifecycle factory fields

* fix(sdk): preserve lifecycle patch extras types

* test(zalouser): widen lifecycle status sink

* test(irc): avoid shadowed status patch

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

* fix(gateway): accept explicit channel ready recovery

* test(qa): assert terminal Slack block fact

* test(qa): restore Slack blocked lifecycle scenario

* test(gateway): lock explicit lifecycle recovery contract
2026-08-03 12:39:48 -07:00
Vincent Koc 21a767639f fix(plugins): unify HTTP route conflict handling (#118203)
* fix(plugins): unify HTTP route conflict handling

* fix(plugins): preserve source-less route replacement

* test(line): type webhook route source
2026-08-04 02:15:00 +08:00
Peter Steinberger 4c66ed5615 feat(channels): lifecycle wave 2 — fifteen more channels publish recorded lifecycle (#118298)
* feat(channels): publish lifecycle from existing status sinks

* feat(channels): thread lifecycle through provider monitors

* fix(mattermost): keep lifecycle status helper private

* fix(matrix): preserve terminal lifecycle during startup cleanup

* fix(matrix): reject invalid tokens during startup

* test(mattermost): drop unused vi import
2026-08-02 17:30:56 -07:00
Peter Steinberger ae22ba5fbb fix(googlechat): consume stale approval card actions (#118225) 2026-08-02 15:25:15 -07:00
Masato Hoshino 570182b449 fix(googlechat): report a blocked account instead of a webhook path that was never bound (#117986)
An unparseable webhookUrl resolves to no path, and the monitor bails before it
creates ingress or registers the HTTP route. The gateway still published the
default path, and because this channel never sets connected, health evaluation
read healthy for the lifetime of the process while inbound delivery was dead.

Make resolveGoogleChatWebhookPath return string | null and have the gateway
publish lifecycle: blocked with lastError instead of a path. The status store
patch-merges, so the blocked branch clears webhookPath explicitly.
2026-08-02 07:01:24 -07:00
Peter Steinberger f7ddcdbf26 refactor: consolidate standard raw-event ingress monitors (#117859)
* refactor(ingress): consolidate standard raw-event monitors

* fix(ingress): preserve monitor callback typing

* fix(ingress): keep standard profile within line limits

* chore(plugin-sdk): regenerate API baseline for shared ingress profile
2026-08-01 23:32:28 -07:00
Javier Ailbirt 7563e40e47 fix(googlechat): drop invalid thread resource names before send (#108324)
Reply routing can hand sendGoogleChatMessage a `thread` that is not a
valid `spaces/{space}/threads/{thread}` resource name — a bare id, a
`spaces/{space}/messages/{message}` name, or a thread from a different
(or wrongly-cased) space. The Chat API rejects the whole request with
`400 INVALID_ARGUMENT`, so the reply is never delivered even though the
agent already produced it (and did any side effects).

Guard the send so the `thread` field and the messageReplyOption fallback
are applied only when the thread is a well-formed name belonging to the
target space; otherwise post to the space as a new thread. Revives the
approach of #28153 (auto-closed as stale) and addresses the failure mode
behind #64313.
2026-08-01 11:59:08 -07:00
Yiğit ERDOĞAN 9dfbf6a8a0 fix(googlechat): cancel unread auth response bodies before release (#115873)
The size guard in readGoogleAuthResponseBytes inspects content-length and
throws before it reaches response.body.getReader(), so an oversized auth
response leaves the stream untouched and the finally block releases the
dispatcher without cancelling it.

This is the same shape #111290 just fixed in api.ts, applied to the auth
transport's own guard site.
2026-07-31 20:54:06 -07:00
Peter Steinberger 9e4381eb1c refactor(channels): unify bundled channel setup contracts (#117106)
* refactor(channels): canonicalize bundled setup contracts

* test(matrix): use scoped environment fixtures
2026-07-31 16:57:29 -07:00
Peter Steinberger 3dce01fa20 fix(googlechat): retain fallback thread continuity (#117053) 2026-07-31 14:54:16 -07:00
Peter Steinberger a5c8c5b3ec fix(googlechat): record canonical receipt thread (#116717)
* fix(googlechat): preserve receipt thread identity

* test(googlechat): prove receipt thread mirroring
2026-07-31 02:07:34 -07:00
Eden 601a405430 fix(channels): mark durable webhook acceptance on Zalo, Google Chat, SMS, Feishu, Nextcloud Talk, and Synology Chat (#115586)
* fix(channels): extend the durable-acceptance marker to zalo, googlechat, and sms

* fix(feishu): mark durable webhook acceptance

* fix(channels): extend the durable-acceptance marker to nextcloud-talk and synology-chat

* fix(channels): extend the durable-acceptance marker to zalo, googlechat, and sms

* fix(feishu): mark durable webhook acceptance

* fix(channels): extend the durable-acceptance marker to nextcloud-talk and synology-chat

* fix(channels): carry durable webhook admission results

* docs(changelog): note durable webhook acceptance

* test(channels): tighten webhook admission types

* chore: leave release changelog to release prep

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-30 01:07:08 +08:00
NIO 0316368518 fix(googlechat): cancel unread fetchOk bodies before release (#111290)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-29 07:34:19 -04:00
Peter Steinberger 986e5d1758 refactor(channels): share ingress retention defaults (#115824) 2026-07-29 06:24:30 -04:00
Peter Steinberger bde6b166c0 refactor(channels): deduplicate ingress monitor shells (#115792)
* refactor(channels): deduplicate ingress monitor shells

* fix(channels): preserve ingress error overload types

* fix(channels): type ingress error overload implementation

* chore(plugin-sdk): ratchet ingress error surface
2026-07-29 05:33:00 -04:00
Peter Steinberger c402688894 feat(media): probe duration and dimensions for playback metadata (#115728)
* feat(media): probe playback metadata

* fix(media): satisfy CI gates

* fix(media): satisfy lint rules
2026-07-29 04:52:17 -04:00
Peter Steinberger 302f262e6b refactor: deduplicate extension normalization primitives (#115650)
* refactor(plugins): reuse SDK normalization primitives

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

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

* fix(ci): use matrix evidence export

* fix(ci): validate matrix evidence artifact
2026-07-29 04:10:06 -04:00
Peter Steinberger 26aa0f5e59 fix(googlechat): surface failed replies without duplicate sends (#115638)
* fix(googlechat): surface reply send failures instead of silently dropping chunks

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

* test(googlechat): narrow regex match index for noUncheckedIndexedAccess

* fix(googlechat): isolate outbound delivery status reporting

---------

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-29 02:00:06 -04:00
wangmiao0668000666 9dbfeb7e61 fix(channels): keep healthy accounts' message actions when one credential SecretRef fails (#110329)
* fix(channels): isolate unavailable account discovery

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>

* refactor(channels): remove stale discovery exports

* test(pr): provide ripgrep fixture command

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-28 01:50:51 -04:00
sunlit-deng 7ebd486ed1 fix(googlechat): media downloads can grow unbounded without a size cap (#98425)
* fix(googlechat): bound uncapped media downloads

* fix(googlechat): align media download cap
2026-07-27 23:29:00 -04:00
Peter Steinberger a1e0c9ea09 refactor(packages): DM-policy contract suite, dead routes, package folds (#114776)
* test(channels): share DM policy contract suite

* refactor(memory): remove dead host CLI routes

* refactor(acp): use canonical normalization helpers

* refactor(web): fold provider runtime into core

* refactor(packages): remove dead runtime exports

* chore(knip): drop retired ACP entry roots
2026-07-27 20:20:13 -04:00
Peter Steinberger 08b1ff73e2 refactor(channels): fix account resolution and setup isolation (#114395)
* refactor(channels): fix account resolution and setup isolation

* fix(channels): keep account resolvers safely destructurable
2026-07-27 03:49:37 -04:00
Peter Steinberger a5d758e74b refactor(plugin-sdk): share channel DM policy setup (#113537) 2026-07-25 01:00:53 -07:00
Peter Steinberger e87b3cea18 refactor(plugin-sdk): share doctor key-move builder (#113539) 2026-07-25 00:21:15 -07:00
Peter Steinberger aae10cade5 refactor(markdown): unify format profile declarations (#113254)
* refactor(markdown): unify format profile declarations

* test(markdown): avoid suppression inventory drift
2026-07-24 00:37:59 -07:00
Peter Steinberger 09f5f2d305 fix(googlechat): render outbound markdown for Chat (#113024)
* fix(googlechat): render outbound markdown dialect

* fix(googlechat): type rendered chunks

* fix(googlechat): satisfy renderer checks
2026-07-23 06:25:20 -07: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 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 83303f1ba8 feat(channels): batch 1 producers drop media placeholder bodies (#111447)
* feat(channels): batch 1 producers drop media placeholder bodies

Media-placeholder program batch 1: Google Chat, Zalo, LINE, and
Mattermost stop minting <media:kind> placeholder bodies. Media-only
messages carry an empty caption plus one structured fact per native
attachment (type-only when a download fails or is rejected, so payload
positions and kind signals stay aligned). The shared
formatMediaPlaceholderText SDK formatter renders text-only carriers
(Mattermost pending-room lines) from structured facts; per-channel
placeholder builders and the expected-count side channel are deleted.

* fix(mattermost): satisfy type, deadcode, and SDK manifest gates
2026-07-19 10:32:09 -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 31423de908 fix(channels): preserve ingress shutdown and named-account setup (#111449)
* fix(channels): repair plugin prerelease regressions

* chore: leave release notes to release automation

* fix(channels): update plugin SDK API baseline
2026-07-19 07:57:37 -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
tiffanychum feb6da25f5 fix(googlechat): keep accounts.default streaming when doctor migrates named accounts (#106018)
* fix(googlechat): keep accounts.default streaming when doctor migrates named accounts

* fix(googlechat): canonicalize migrated streaming

Replace the layered doctor migration with canonical root/default/account materialization and prove runtime resolution matches the stored config.\n\nCo-authored-by: tiffanychum <71036662+tiffanychum@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-18 19:05:57 -07:00
Peter Steinberger 7562b79465 refactor(channels): share durable ingress monitor (#111017)
* refactor(channels): share durable ingress monitor

* fix(channels): harden ingress monitor shutdown

* docs: refresh SDK documentation map
2026-07-19 00:42:08 +01:00
Peter Steinberger a89086282a fix(googlechat): preserve webhook messages across restarts (#110833)
* feat(googlechat): add durable ingress drain

* fix(googlechat): retain webhook dedupe horizon

* fix(googlechat): break ingress type cycle
2026-07-18 18:26:28 +01:00