feat: searchable timezone select & bot timezone priority

- Add reusable TimezoneSelect component with search and UTC offset labels
- Replace plain Select with searchable TimezoneSelect in profile settings,
  bot settings, and browser context settings
- Move bot timezone setting from header dialog into bot settings tab
- Resolve timezone with bot > user > system priority for all LLM-facing
  time formatting (user message header, system prompt, heartbeat, tools,
  memory extraction)
- Format tool output timestamps (history, contacts) in resolved timezone
This commit is contained in:
Acbox
2026-03-26 21:00:21 +08:00
parent 65b2797626
commit da2e999ce3
18 changed files with 259 additions and 206 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ func (p *ContactsProvider) Tools(_ context.Context, session SessionContext) ([]s
"conversation_type": r.ConversationType,
"target": r.ReplyTarget,
"conversation_id": r.ConversationID,
"last_active": r.UpdatedAt.Format("2006-01-02T15:04:05Z"),
"last_active": sess.FormatTime(r.UpdatedAt),
}
if len(r.Metadata) > 0 {
if v, ok := r.Metadata["conversation_name"].(string); ok && v != "" {
+3 -3
View File
@@ -166,8 +166,8 @@ func (p *HistoryProvider) execListSessions(ctx context.Context, sess SessionCont
"platform": s.ChannelType,
"route_id": s.RouteID,
"conversation_type": s.RouteConversationType,
"last_active": s.UpdatedAt.Format(time.RFC3339),
"created_at": s.CreatedAt.Format(time.RFC3339),
"last_active": sess.FormatTime(s.UpdatedAt),
"created_at": sess.FormatTime(s.CreatedAt),
}
if m := s.RouteMetadata; len(m) > 0 {
@@ -261,7 +261,7 @@ func (p *HistoryProvider) execSearchMessages(ctx context.Context, sess SessionCo
"session_id": row.SessionID.String(),
"role": row.Role,
"text": text,
"created_at": row.CreatedAt.Time.Format(time.RFC3339),
"created_at": sess.FormatTime(row.CreatedAt.Time),
}
if dbpkg.TextToString(row.Platform) != "" {
entry["platform"] = dbpkg.TextToString(row.Platform)
+10
View File
@@ -6,6 +6,7 @@ import (
"fmt"
"math"
"strings"
"time"
sdk "github.com/memohai/twilight-ai/sdk"
)
@@ -28,6 +29,15 @@ type SessionContext struct {
SupportsImageInput bool
IsSubagent bool
Skills map[string]SkillDetail
TimezoneLocation *time.Location
}
// FormatTime formats a time.Time using the session timezone (falls back to UTC).
func (s SessionContext) FormatTime(t time.Time) string {
if s.TimezoneLocation != nil {
t = t.In(s.TimezoneLocation)
}
return t.Format(time.RFC3339)
}
// ToolProvider supplies a set of tools for the agent.