Skip to content
Open
5 changes: 5 additions & 0 deletions cmd/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -818,6 +818,11 @@ func runGateway() {
// Register channels/instances/links/teams RPC methods
wireChannelRPCMethods(server, pgStores, channelMgr, agentRouter, msgBus)

// Register party mode WS RPC methods
if pgStores.Party != nil {
methods.NewPartyMethods(pgStores.Party, pgStores.Agents, providerRegistry, msgBus).Register(server.Router())
}

// Wire channel event subscribers (cache invalidation, pairing, cascade disable)
wireChannelEventSubscribers(msgBus, server, pgStores, channelMgr, instanceLoader, pairingMethods, cfg)

Expand Down
2 changes: 1 addition & 1 deletion internal/agent/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
reminder := "[System] " + strings.Join(parts, "\n\n")
messages = append(messages,
providers.Message{Role: "user", Content: reminder},
providers.Message{Role: "assistant", Content: "I see the task status. Let me handle accordingly."},
// No assistant prefill — thinking models reject it.
)
}
}
Expand Down
3 changes: 2 additions & 1 deletion internal/agent/loop_history.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func limitHistoryTurns(msgs []providers.Message, limit int) []providers.Message
// - Orphaned tool messages at start of history (after truncation)
// - tool_result without matching tool_use in preceding assistant message
// - assistant with tool_calls but missing tool_results
// sanitizeHistory repairs tool_use/tool_result pairing in session history.
//
// Returns the cleaned messages and the number of messages that were dropped or synthesized.
func sanitizeHistory(msgs []providers.Message) ([]providers.Message, int) {
if len(msgs) == 0 {
Expand All @@ -297,6 +297,7 @@ func sanitizeHistory(msgs []providers.Message) ([]providers.Message, int) {
"tool_call_id", msgs[start].ToolCallID)
dropped++
start++
dropped++
}

if start >= len(msgs) {
Expand Down
63 changes: 46 additions & 17 deletions internal/channels/zalo/zalo.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (c *Channel) Start(ctx context.Context) error {
if err != nil {
return fmt.Errorf("zalo getMe failed: %w", err)
}
slog.Info("zalo bot connected", "bot_id", info.ID, "bot_name", info.Name)
slog.Info("zalo bot connected", "bot_id", info.ID, "bot_name", info.Label())

c.SetRunning(true)

Expand Down Expand Up @@ -418,29 +418,41 @@ type zaloAPIResponse struct {
}

type zaloBotInfo struct {
ID string `json:"id"`
Name string `json:"name"`
ID string `json:"id"`
Name string `json:"account_name"`
DisplayName string `json:"display_name"`
}

func (b *zaloBotInfo) Label() string {
if b.DisplayName != "" {
return b.DisplayName
}
return b.Name
}

type zaloMessage struct {
MessageID string `json:"message_id"`
Text string `json:"text"`
Photo string `json:"photo"`
PhotoURL string `json:"photo_url"`
Caption string `json:"caption"`
From zaloFrom `json:"from"`
Chat zaloChat `json:"chat"`
Date int64 `json:"date"`
MessageID string `json:"message_id"`
MessageType string `json:"message_type"`
Text string `json:"text"`
Photo string `json:"photo"`
PhotoURL string `json:"photo_url"`
Caption string `json:"caption"`
From zaloFrom `json:"from"`
Chat zaloChat `json:"chat"`
Date int64 `json:"date"`
}

type zaloFrom struct {
ID string `json:"id"`
Username string `json:"username"`
ID string `json:"id"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
IsBot bool `json:"is_bot"`
}

type zaloChat struct {
ID string `json:"id"`
Type string `json:"type"`
ID string `json:"id"`
Type string `json:"type"`
ChatType string `json:"chat_type"`
}

type zaloUpdate struct {
Expand Down Expand Up @@ -514,11 +526,28 @@ func (c *Channel) getUpdates(timeout int) ([]zaloUpdate, error) {
return nil, err
}

// Try array first
var updates []zaloUpdate
if err := json.Unmarshal(result, &updates); err != nil {
if err := json.Unmarshal(result, &updates); err == nil {
return updates, nil
}

// Try single object (Zalo Bot Platform returns one update at a time)
var single zaloUpdate
if err := json.Unmarshal(result, &single); err == nil && single.EventName != "" {
slog.Info("zalo update received", "event", single.EventName)
return []zaloUpdate{single}, nil
}

// Try wrapped {"updates": [...]}
var wrapped struct {
Updates []zaloUpdate `json:"updates"`
}
if err := json.Unmarshal(result, &wrapped); err != nil {
slog.Warn("zalo getUpdates unknown format", "raw", string(result[:min(len(result), 500)]))
return nil, fmt.Errorf("unmarshal updates: %w", err)
}
return updates, nil
return wrapped.Updates, nil
}

func (c *Channel) sendMessage(chatID, text string) error {
Expand Down
Loading