time_context.go RawHistoryFolder
1package core
2
3import (
4 "fmt"
5 "strconv"
6 "strings"
7 "time"
8)
9
10type agentTimeContext struct {
11 started time.Time
12 location *time.Location
13 zone string
14 offset string
15}
16
17func newAgentTimeContext(now time.Time) agentTimeContext {
18 zoneName, offset := localTimeMetadata(now)
19 return agentTimeContext{
20 started: now,
21 location: now.Location(),
22 zone: zoneName,
23 offset: offset,
24 }
25}
26
27func localTimeMetadata(now time.Time) (string, string) {
28 zone, seconds := now.Zone()
29 zoneName := now.Location().String()
30 if zoneName == "" || zoneName == "Local" {
31 zoneName = zone
32 }
33 return zoneName, formatUTCOffset(seconds)
34}
35
36func (a *Agent) SetSessionTimeContext(started time.Time, timezone, offset string) {
37 if started.IsZero() {
38 return
39 }
40 location := time.Local
41 loadedTimezone := false
42 if timezone != "" && timezone != "Local" {
43 if loaded, err := time.LoadLocation(timezone); err == nil {
44 location = loaded
45 loadedTimezone = true
46 }
47 }
48 if !loadedTimezone && offset != "" {
49 if fixed := parseUTCOffset(offset); fixed != nil {
50 location = fixed
51 }
52 }
53 if timezone == "" {
54 timezone = location.String()
55 }
56 if offset == "" {
57 _, seconds := started.In(location).Zone()
58 offset = formatUTCOffset(seconds)
59 }
60
61 a.mu.Lock()
62 a.timeContext = agentTimeContext{
63 started: started,
64 location: location,
65 zone: timezone,
66 offset: offset,
67 }
68 a.hasSessionTimeContext = true
69 a.mu.Unlock()
70}
71
72func (a *Agent) providerTimeContext() agentTimeContext {
73 a.mu.Lock()
74 defer a.mu.Unlock()
75 if !a.hasSessionTimeContext {
76 return agentTimeContext{}
77 }
78 return a.timeContext
79}
80
81func (c agentTimeContext) systemText() string {
82 return c.developerText()
83}
84
85// developerText returns host context projected after stable instructions.
86// Minute precision is sufficient for coordination and avoids generating a
87// unique timestamp solely to distinguish otherwise-equal agents.
88func (c agentTimeContext) developerText() string {
89 if c.started.IsZero() {
90 return ""
91 }
92 location := c.location
93 if location == nil {
94 location = time.UTC
95 }
96 zone := c.zone
97 if zone == "" {
98 zone = location.String()
99 }
100 offset := c.offset
101 if offset == "" {
102 _, seconds := c.started.In(location).Zone()
103 offset = formatUTCOffset(seconds)
104 }
105 return fmt.Sprintf("[Session time context]\nsession_started: %s\nlocal_timezone: %s (UTC%s)", c.started.In(location).Format("2006-01-02 15:04"), zone, offset)
106}
107
108func formatUTCOffset(seconds int) string {
109 sign := "+"
110 if seconds < 0 {
111 sign = "-"
112 seconds = -seconds
113 }
114 return fmt.Sprintf("%s%02d:%02d", sign, seconds/3600, (seconds%3600)/60)
115}
116
117func parseUTCOffset(value string) *time.Location {
118 if len(value) != len("+00:00") || (value[0] != '+' && value[0] != '-') || value[3] != ':' {
119 return nil
120 }
121 hours, errHours := strconv.Atoi(value[1:3])
122 minutes, errMinutes := strconv.Atoi(value[4:6])
123 if errHours != nil || errMinutes != nil || hours > 23 || minutes > 59 {
124 return nil
125 }
126 seconds := hours*3600 + minutes*60
127 if strings.HasPrefix(value, "-") {
128 seconds = -seconds
129 }
130 return time.FixedZone(value, seconds)
131}