66 "encoding/json"
77 "fmt"
88 "log/slog"
9+ "strconv"
910 "sync"
1011 "time"
1112
@@ -15,6 +16,7 @@ import (
1516 "github.com/peerclaw/peerclaw-agent/filetransfer"
1617 "github.com/peerclaw/peerclaw-agent/peer"
1718 "github.com/peerclaw/peerclaw-agent/platform"
19+ "github.com/peerclaw/peerclaw-agent/sdkversion"
1820 "github.com/peerclaw/peerclaw-agent/security"
1921 pcsignaling "github.com/peerclaw/peerclaw-agent/signaling"
2022 "github.com/peerclaw/peerclaw-agent/transport"
@@ -165,6 +167,7 @@ type Agent struct {
165167 logger * slog.Logger
166168 mu sync.RWMutex
167169 running bool
170+ versionWarned sync.Once
168171 stopNonceCleaner context.CancelFunc
169172}
170173
@@ -306,6 +309,15 @@ func (a *Agent) Start(ctx context.Context) error {
306309 // Derive agent ID from public key without server registration.
307310 a .agentID = a .keypair .PublicKeyString ()
308311 } else {
312+ // Build platform metadata for registration.
313+ var regMeta map [string ]string
314+ if a .opts .Platform != nil {
315+ regMeta = map [string ]string {
316+ "platform_name" : a .opts .Platform .Name (),
317+ "platform_protocol" : strconv .Itoa (a .opts .Platform .ProtocolVersion ()),
318+ }
319+ }
320+
309321 // Register with the platform.
310322 var regErr error
311323 if a .opts .ClaimToken != "" {
@@ -326,6 +338,7 @@ func (a *Agent) Start(ctx context.Context) error {
326338 Protocols : a .opts .Protocols ,
327339 Endpoint : discovery.EndpointReq {URL : "p2p://" + a .keypair .PublicKeyString ()},
328340 Signature : sig ,
341+ Metadata : regMeta ,
329342 })
330343 if err != nil {
331344 regErr = fmt .Errorf ("claim register: %w" , err )
@@ -340,6 +353,7 @@ func (a *Agent) Start(ctx context.Context) error {
340353 Capabilities : a .Capabilities (),
341354 Endpoint : discovery.EndpointReq {URL : "p2p://" + a .keypair .PublicKeyString ()},
342355 Protocols : a .opts .Protocols ,
356+ Metadata : regMeta ,
343357 })
344358 if err != nil {
345359 regErr = fmt .Errorf ("register with platform: %w" , err )
@@ -538,6 +552,18 @@ func (a *Agent) Start(ctx context.Context) error {
538552 // Initialize platform adapter integration.
539553 if a .opts .Platform != nil {
540554 pa := a .opts .Platform
555+
556+ // Verify adapter protocol compatibility before connecting.
557+ if err := platform .CheckProtocolVersion (pa ); err != nil {
558+ a .mu .Lock ()
559+ a .running = false
560+ a .mu .Unlock ()
561+ return fmt .Errorf ("platform compatibility: %w" , err )
562+ }
563+
564+ // Advisory check: warn if SDK is outside adapter's declared compat range.
565+ platform .CheckSDKCompat (pa , a .logger )
566+
541567 pa .SetOutboundHandler (func (sessionKey , text string ) {
542568 peerID := platform .ParsePeerFromSessionKey (sessionKey )
543569 if peerID == "" {
@@ -550,6 +576,17 @@ func (a *Agent) Start(ctx context.Context) error {
550576 a .logger .Warn ("platform connect failed" , "platform" , pa .Name (), "error" , err )
551577 } else {
552578 a .platformAdapter = pa
579+
580+ // Log platform adapter compatibility summary.
581+ attrs := []any {
582+ "platform" , pa .Name (),
583+ "protocol_version" , pa .ProtocolVersion (),
584+ "sdk_version" , sdkversion .Version ,
585+ }
586+ if v , ok := pa .(platform.Versioned ); ok {
587+ attrs = append (attrs , "plugin_version" , v .PluginVersion ())
588+ }
589+ a .logger .Info ("platform adapter connected" , attrs ... )
553590 }
554591
555592 // Forward notifications to platform.
@@ -573,6 +610,11 @@ func (a *Agent) Start(ctx context.Context) error {
573610 }
574611 }
575612
613+ // Check for SDK updates in the background.
614+ if ! a .opts .SkipRegistration {
615+ go a .checkForUpdates (ctx )
616+ }
617+
576618 a .logger .Info ("agent started" , "id" , a .agentID , "name" , a .opts .Name , "pubkey" , a .keypair .PublicKeyString ())
577619 return nil
578620}
@@ -642,6 +684,27 @@ func (a *Agent) Stop(ctx context.Context) error {
642684 return nil
643685}
644686
687+ // checkForUpdates sends a heartbeat and logs a warning if a newer SDK is available.
688+ func (a * Agent ) checkForUpdates (ctx context.Context ) {
689+ regClient , ok := a .discovery .(* discovery.RegistryClient )
690+ if ! ok {
691+ return
692+ }
693+ resp , err := regClient .Heartbeat (ctx , a .agentID , "online" )
694+ if err != nil {
695+ return
696+ }
697+ if resp .VersionAdvisory != nil && resp .VersionAdvisory .SDKUpdateAvailable {
698+ a .versionWarned .Do (func () {
699+ a .logger .Warn ("a newer PeerClaw SDK is available" ,
700+ "current" , sdkversion .Version ,
701+ "latest" , resp .VersionAdvisory .LatestSDK ,
702+ "release_url" , resp .VersionAdvisory .ReleaseURL ,
703+ )
704+ })
705+ }
706+ }
707+
645708// Send sends an envelope to a peer using P2P (preferred) or signaling relay (fallback).
646709// The payload is encrypted (if a session key exists), then the envelope is signed
647710// covering the ciphertext + headers (encrypt-then-sign for pre-authentication).
0 commit comments