feat: make page/admin policy + owner changes take effect live (onConfigEvent + owner sync + reported-policy push) - #5
Merged
Conversation
Port two logics from zylos-openmax (source of truth) so page/admin-configured
permissions actually take effect in the openclaw-openmax bridge.
The openclaw SDK (CwsAgentBridge) already classifies agent.config.* system
frames and applies the "not for us" target check, then hands them to
callbacks.onConfigEvent(orgConfig, {event,data,frame}). The plugin was not
supplying that callback, so all six policy events + owner_changed were silently
dropped. Wire it in.
Logic 1 — six agent.config.* access events → live config + persist:
- applyConfigEvent() in helpers.ts (pure, unit-tested): dm_policy_changed,
dm_allowlist_changed, group_mode_changed, group_allowfrom_changed,
group_scope_changed, group_allowlist_changed → mutate OpenMaxAccessConfig.
- handleConfigEvent() in index.ts mutates the SAME orgConfig.access object the
SDK's decideInbound gate reads (live effect, no restart) then persistAccess()
writes it to config.json (durability across restart), mirroring persistOwner.
Logic 2 — owner sync:
- owner_changed event → syncOwnerFromCore(): fetch /members/{self.member_id}
from cws-core, sync self.display_name + owner into the live orgConfig, persist
via the existing persistOwner path. Never clears a local owner when core has
none (preserves the SDK first-DM auto-bind fallback).
- Periodic 5-min owner/display-name sync timer (OWNER_SYNC_INTERVAL_MS),
unref'd, cleared in stopBridge — matches zylos-openmax's owner-config-sync
task; the SDK arms no owner timer of its own. Event-driven path is kept.
Tests: 7 new applyConfigEvent cases (13 total, all pass under `npm test`).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Port zylos-openmax's syncConfigToComm (comm-bridge.js:1875-1912): the agent
PUTs its local DM/group access policy to cws-comm's
/agents/{memberId}/reported-policy so the server reflects offline config.json
edits or a fresh install's pre-populated policy.
- helpers.ts: buildReportedPolicy(access) — pure payload builder mirroring
zylos's shape/defaults exactly (dm_policy/dm_allowlist/group_scope/
group_allowlist/groups[]; dmPolicy→'owner', groupPolicy→'allowlist',
per-group mode→'mention', allowFrom→['*']).
- index.ts: syncConfigToComm(orgConfig) uses http.putForOrg; 404 → skip quietly
(endpoint not available), matching zylos. Wired into BOTH call sites zylos
uses: the config-change epilogue in handleConfigEvent (immediate report,
mirrors comm-bridge.js:1300) and the 5-min ownerSyncTimer tick after the
owner pull (mirrors periodicSync).
Tests: 2 new buildReportedPolicy cases (15 total, all pass under `npm test`).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
zylos-luna-coco
approved these changes
Jul 22, 2026
zylos-luna-coco
left a comment
There was a problem hiding this comment.
APPROVED.
Reviewed 428 lines across 3 files. Clean port of the zylos-openmax config-event + owner-sync + reported-policy-push pattern to OpenClaw.
Architecture is sound:
- Pure helpers in
helpers.ts(testable without host):applyConfigEventhandles all 6 access events with proper validation against VALID_* sets;buildReportedPolicybuilds the reverse-push payload with correct zylos defaults. - Integration in
index.ts: live mutation of the sameorgConfigobject the SDK gate reads (no restart needed), clone-before-write persist for durability, and the 3-layer owner convergence (event-driven + periodic 5-min timer + reconnect) consistent with hermes-openmax PR #10.
Key correctness points verified:
owner_changedcorrectly routed tosyncOwnerFromCore(notapplyConfigEvent) — treated as refresh hint, Core is authoritative.- Never clears local owner when Core reports none — preserves first-DM auto-bind fallback.
persistAccessusesstructuredClonebefore write — safe from mutation bugs.- Timer
.unref()— won't keep the process alive; properly cleared instopBridge. syncConfigToComm404 → skip quietly (endpoint may not exist on older cws-comm).
Test coverage (105 lines): all 6 access event types + edge cases (invalid policies, empty arrays, idempotent adds, silent-mode deletion, unknown events). owner_changed explicitly asserted as not an access mutation. buildReportedPolicy tested with both empty and populated access.
No issues found.
Co-Reviewed-By: luna.coco
Merged
athanbase
added a commit
that referenced
this pull request
Jul 22, 2026
Live policy/owner-change propagation (onConfigEvent) + owner sync timer + reported-policy reverse push (PR #5). Bump plugin version 1.0.0 -> 1.1.0 and update clone-tag references in README + onboarding. Co-authored-by: Zylos <zylos@coco.xyz> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Page/admin-configured permissions were not taking effect: the plugin never registered the SDK's
onConfigEventcallback, so all sixagent.config.*access-change events andowner_changedwere silently dropped (SDK classifies them atorchestrator.js:550, then hands them tocallbacks.onConfigEvent— which was undefined here). Ported the equivalent logic from zylos-openmax (source of truth).Changes
Logic 1 — change-message → live config sync
onConfigEvent→handleConfigEvent.dm_policy/dm_allowlist/group_mode/group_allowfrom/group_scope/group_allowlist) mutateorgConfig.accessin place — the same object the SDK'sdecideInboundreads per inbound message, so gates re-read immediately, no restart — then persist to config.json (persistAccess).applyConfigEvent(access, event, data)inhelpers.ts(unit-tested).Logic 2 — owner sync
owner_changed→syncOwnerFromCore: fetch/members/{self}, sync owner + display_name into liveorgConfig+persistOwner. Never clears a local owner when core has none (preserves first-DM auto-bind).ownerSyncTimer(unref'd, cleared on shutdown) heals missed events; event-driven path retained. Matches zylos-openmax'sowner-config-synctimer cadence.Reverse push — local policy → cws-comm
syncConfigToComm:PUT /agents/{memberId}/reported-policyviahttp.putForOrg, payload shape mirrors zylos byte-for-byte (dm_policy/dm_allowlist/group_scope/group_allowlist/groups[]). 404 → skip quietly (endpoint may land later). Called on every access change (config-event epilogue) and in the 5-min tick, mirroring zylos's two call sites.Tests
npm test→ 15 pass / 0 fail (+2 forbuildReportedPolicy, existing +7 forapplyConfigEvent).helpers.tsstrict-typechecks clean.Notes / open items
datafield names follow the zylos source; if cws-comm's live payload keys differ, extraction needs adjusting (graceful no-op on mismatch).reported-policyendpoint must accept the same field names on the openclaw deployment; degrades gracefully (404 → skip) if not yet deployed.Commits:
8dc71f9(logics 1+2),8550879(reverse push).