fix(bridge): reconstruct relay reply context (#514)

This commit is contained in:
xxb authored and GitHub committed 2026-04-09 18:56:11 +08:00
1 parent c41e3f4ba3
commit d5ce331ea7
2 files changed
+101 -1

No files matched your search

+44 -1
View File
@@ -60,6 +60,18 @@ type bridgeReplyCtx struct {
ReplyCtx string `json:"reply_ctx"`
}
const bridgeReconstructReplyCtxKind = "bridge_reconstruct"
// bridgeReconstructReplyCtxPayload is a forward-compatible reply envelope for
// reconstruct_reply adapters. Receivers should ignore unknown fields.
type bridgeReconstructReplyCtxPayload struct {
Kind string `json:"kind"`
Version int `json:"v"`
SenderProject string `json:"sender_project"`
TransportChatID string `json:"transport_chat_id"`
TransportSessionKey string `json:"transport_session_key,omitempty"`
}
// --- Wire protocol messages ---
type bridgeMsg struct {
@@ -305,13 +317,44 @@ func (bp *BridgePlatform) ReconstructReplyCtx(sessionKey string) (any, error) {
if !a.capabilities["reconstruct_reply"] {
return nil, fmt.Errorf("bridge: adapter %q does not support reconstruct_reply", platform)
}
replyCtx, err := buildBridgeReconstructReplyCtx(bp.project, sessionKey)
if err != nil {
return nil, err
}
return &bridgeReplyCtx{
Platform: platform,
SessionKey: sessionKey,
ReplyCtx: sessionKey,
ReplyCtx: replyCtx,
}, nil
}
func buildBridgeReconstructReplyCtx(project, sessionKey string) (string, error) {
chatID, err := bridgeTransportChatID(sessionKey)
if err != nil {
return "", err
}
payload := bridgeReconstructReplyCtxPayload{
Kind: bridgeReconstructReplyCtxKind,
Version: 1,
SenderProject: project,
TransportChatID: chatID,
TransportSessionKey: sessionKey,
}
data, err := json.Marshal(payload)
if err != nil {
return "", fmt.Errorf("bridge: marshal reconstruct reply ctx: %w", err)
}
return string(data), nil
}
func bridgeTransportChatID(sessionKey string) (string, error) {
parts := strings.SplitN(sessionKey, ":", 3)
if len(parts) < 2 || parts[1] == "" {
return "", fmt.Errorf("bridge: invalid session key %q", sessionKey)
}
return parts[1], nil
}
func (bp *BridgePlatform) SendCard(ctx context.Context, replyCtx any, card *Card) error {
rc, ok := replyCtx.(*bridgeReplyCtx)
if !ok {
+57
View File
@@ -252,6 +252,63 @@ func TestBridge_ReplyRouting(t *testing.T) {
}
}
func TestBridge_ReconstructReplyCtx_RequiresCapability(t *testing.T) {
bs, wsURL := startTestBridge(t, "")
bp := bs.NewPlatform("advisor-gemini")
conn := dialWS(t, wsURL, nil)
register(t, conn, "bridge", []string{"text"})
_, err := bp.ReconstructReplyCtx("bridge:1491487450722341088:relay")
if err == nil || !strings.Contains(err.Error(), "does not support reconstruct_reply") {
t.Fatalf("ReconstructReplyCtx() error = %v, want reconstruct_reply capability error", err)
}
}
func TestBridge_ReconstructReplyCtx_UsesStructuredPayload(t *testing.T) {
bs, wsURL := startTestBridge(t, "")
bp := bs.NewPlatform("advisor-gemini")
conn := dialWS(t, wsURL, nil)
register(t, conn, "bridge", []string{"text", "reconstruct_reply"})
replyCtx, err := bp.ReconstructReplyCtx("bridge:1491487450722341088:relay")
if err != nil {
t.Fatalf("ReconstructReplyCtx() error = %v", err)
}
rc, ok := replyCtx.(*bridgeReplyCtx)
if !ok {
t.Fatalf("reply ctx type = %T, want *bridgeReplyCtx", replyCtx)
}
if rc.Platform != "bridge" {
t.Fatalf("Platform = %q, want bridge", rc.Platform)
}
if rc.SessionKey != "bridge:1491487450722341088:relay" {
t.Fatalf("SessionKey = %q, want relay session key", rc.SessionKey)
}
var payload bridgeReconstructReplyCtxPayload
if err := json.Unmarshal([]byte(rc.ReplyCtx), &payload); err != nil {
t.Fatalf("unmarshal reply_ctx: %v", err)
}
if payload.Kind != bridgeReconstructReplyCtxKind {
t.Fatalf("kind = %q, want %q", payload.Kind, bridgeReconstructReplyCtxKind)
}
if payload.Version != 1 {
t.Fatalf("version = %d, want 1", payload.Version)
}
if payload.SenderProject != "advisor-gemini" {
t.Fatalf("sender_project = %q, want advisor-gemini", payload.SenderProject)
}
if payload.TransportChatID != "1491487450722341088" {
t.Fatalf("transport_chat_id = %q, want 1491487450722341088", payload.TransportChatID)
}
if payload.TransportSessionKey != "bridge:1491487450722341088:relay" {
t.Fatalf("transport_session_key = %q, want relay session key", payload.TransportSessionKey)
}
}
func TestBridge_CardFallback(t *testing.T) {
bs, wsURL := startTestBridge(t, "")