1
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
package api
2
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
3
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
import (
4
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"encoding/json"
5
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"fmt"
6
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"net/http"
7
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"os"
8
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"sync"
9
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
10
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"github.com/conflicthq/scuttlebot/internal/store"
11
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
)
12
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
13
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// BehaviorConfig defines a pre-registered system bot behavior.
14
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type BehaviorConfig struct {
15
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID string `json:"id"`
16
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name string `json:"name"`
17
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description string `json:"description"`
18
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick string `json:"nick"`
19
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Enabled bool `json:"enabled"`
20
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels bool `json:"join_all_channels"`
21
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ExcludeChannels []string `json:"exclude_channels"`
22
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RequiredChannels []string `json:"required_channels"`
23
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Config holds bot-specific configuration. The schema is defined per bot
24
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// in the UI; the backend stores and returns it opaquely.
25
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Config map[string]any `json:"config,omitempty"`
26
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
27
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
28
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// AgentPolicy defines requirements applied to all registering agents.
29
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type AgentPolicy struct {
30
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RequireCheckin bool `json:"require_checkin"`
31
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
CheckinChannel string `json:"checkin_channel"`
32
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RequiredChannels []string `json:"required_channels"`
33
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
OnlineTimeoutSecs int `json:"online_timeout_secs,omitempty"`
34
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ReapAfterDays int `json:"reap_after_days,omitempty"`
35
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
36
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
37
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// LoggingPolicy configures message logging.
38
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type LoggingPolicy struct {
39
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Enabled bool `json:"enabled"`
40
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Dir string `json:"dir"` // directory to write log files into
41
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Format string `json:"format"` // "jsonl" | "csv" | "text"
42
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Rotation string `json:"rotation"` // "none" | "daily" | "weekly" | "size"
43
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
MaxSizeMB int `json:"max_size_mb"` // size rotation threshold (MiB); 0 = unlimited
44
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
PerChannel bool `json:"per_channel"` // separate file per channel
45
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
MaxAgeDays int `json:"max_age_days"` // prune rotated files older than N days; 0 = keep all
46
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
47
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
48
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// ChannelDisplayConfig holds per-channel rendering preferences.
49
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type ChannelDisplayConfig struct {
50
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
MirrorDetail string `json:"mirror_detail,omitempty"` // "full", "compact", "minimal"
51
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RenderMode string `json:"render_mode,omitempty"` // "rich", "text"
52
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
53
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
54
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// BridgePolicy configures bridge-specific UI/relay behavior.
55
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type BridgePolicy struct {
56
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// WebUserTTLMinutes controls how long HTTP bridge sender nicks remain
57
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// visible in the channel user list after their last post.
58
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
WebUserTTLMinutes int `json:"web_user_ttl_minutes"`
59
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// ChannelDisplay holds per-channel rendering config.
60
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ChannelDisplay map[string]ChannelDisplayConfig `json:"channel_display,omitempty"`
61
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
62
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
63
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// PolicyLLMBackend stores an LLM backend configuration in the policy store.
64
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// This allows backends to be added and edited from the web UI rather than
65
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// requiring a change to scuttlebot.yaml.
66
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
//
67
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// API keys are write-only — GET responses replace them with "***" when set.
68
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type PolicyLLMBackend struct {
69
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name string `json:"name"`
70
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Backend string `json:"backend"`
71
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
APIKey string `json:"api_key,omitempty"`
72
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
BaseURL string `json:"base_url,omitempty"`
73
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Model string `json:"model,omitempty"`
74
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Region string `json:"region,omitempty"`
75
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
AWSKeyID string `json:"aws_key_id,omitempty"`
76
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
AWSSecretKey string `json:"aws_secret_key,omitempty"`
77
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Allow []string `json:"allow,omitempty"`
78
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Block []string `json:"block,omitempty"`
79
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Default bool `json:"default,omitempty"`
80
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
81
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
82
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// ROETemplate is a rules-of-engagement template.
83
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type ROETemplate struct {
84
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name string `json:"name"`
85
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description string `json:"description,omitempty"`
86
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Channels []string `json:"channels,omitempty"`
87
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Permissions []string `json:"permissions,omitempty"`
88
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
RateLimit struct {
89
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
MessagesPerSecond float64 `json:"messages_per_second,omitempty"`
90
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Burst int `json:"burst,omitempty"`
91
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
} `json:"rate_limit,omitempty"`
92
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
93
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
94
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Policies is the full mutable settings blob, persisted to policies.json.
95
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type Policies struct {
96
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Behaviors []BehaviorConfig `json:"behaviors"`
97
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
AgentPolicy AgentPolicy `json:"agent_policy"`
98
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Bridge BridgePolicy `json:"bridge"`
99
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Logging LoggingPolicy `json:"logging"`
100
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
LLMBackends []PolicyLLMBackend `json:"llm_backends,omitempty"`
101
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ROETemplates []ROETemplate `json:"roe_templates,omitempty"`
102
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
OnJoinMessages map[string]string `json:"on_join_messages,omitempty"` // channel → message template
103
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
104
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
105
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// defaultBehaviors lists every built-in bot with conservative defaults (disabled).
106
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
var defaultBehaviors = []BehaviorConfig{
107
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
108
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "auditbot",
109
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Auditor",
110
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Immutable append-only audit trail of agent actions and credential lifecycle events.",
111
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "auditbot",
112
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
113
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
114
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
115
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "scribe",
116
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Scribe",
117
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Records all channel messages to a structured log store.",
118
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "scribe",
119
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
120
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
121
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
122
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "herald",
123
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Herald",
124
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Routes event notifications from external systems to IRC channels.",
125
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "herald",
126
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
127
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
128
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
129
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "oracle",
130
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Oracle",
131
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "On-demand channel summarisation via DM using an LLM.",
132
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "oracle",
133
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
134
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
135
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
136
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "warden",
137
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Warden",
138
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Enforces channel moderation — detects floods and malformed messages, escalates warn → mute → kick.",
139
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "warden",
140
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
141
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
142
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
143
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "scroll",
144
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Scroll",
145
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Replays channel history to users via DM on request.",
146
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "scroll",
147
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
148
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
149
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
150
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "systembot",
151
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Systembot",
152
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Logs IRC system events (joins, parts, quits, mode changes) to a store.",
153
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "systembot",
154
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
155
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
156
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
157
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "snitch",
158
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Snitch",
159
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Watches for erratic behaviour and alerts operators via DM or a dedicated channel.",
160
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "snitch",
161
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
162
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
163
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
164
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "sentinel",
165
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Sentinel",
166
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "LLM-powered channel observer. Detects policy violations and posts structured incident reports to a mod channel. Never takes enforcement action.",
167
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "sentinel",
168
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
169
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
170
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
171
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "steward",
172
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Steward",
173
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Acts on sentinel incident reports — issues warnings, mutes, or kicks based on severity. Operators can also issue direct commands via DM.",
174
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "steward",
175
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
JoinAllChannels: true,
176
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
177
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{
178
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ID: "shepherd",
179
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Name: "Shepherd",
180
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description: "Goal-directed agent coordinator. Assigns work, tracks progress, checks in on agents, generates plans using LLM. Configurable with any LLM provider.",
181
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Nick: "shepherd",
182
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
183
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
184
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
185
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// BotCommand describes a single command a bot responds to.
186
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type BotCommand struct {
187
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Command string `json:"command"`
188
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Usage string `json:"usage"`
189
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
Description string `json:"description"`
190
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
191
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
192
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// botCommands maps bot ID to its available commands.
193
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
var botCommands = map[string][]BotCommand{
194
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"oracle": {
195
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "summarize", Usage: "summarize #channel [last=N] [format=toon|json]", Description: "Summarize recent channel activity using an LLM."},
196
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
197
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"scroll": {
198
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "replay", Usage: "replay #channel [last=N] [since=<unix_ms>]", Description: "Replay recent channel history via DM."},
199
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
200
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"steward": {
201
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "mute", Usage: "mute <nick> [duration]", Description: "Mute a nick in the current channel."},
202
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "unmute", Usage: "unmute <nick>", Description: "Remove mute from a nick."},
203
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "kick", Usage: "kick <nick> [reason]", Description: "Kick a nick from the current channel."},
204
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "warn", Usage: "warn <nick> <message>", Description: "Send a warning notice to a nick."},
205
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
206
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"warden": {
207
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "status", Usage: "status", Description: "Show warden rate-limit status for all tracked nicks."},
208
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
209
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"snitch": {
210
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "status", Usage: "status", Description: "Show snitch monitoring status and alert history."},
211
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
212
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"herald": {
213
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "announce", Usage: "announce #channel <message>", Description: "Post an announcement to a channel."},
214
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
215
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
"shepherd": {
216
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "goal", Usage: "GOAL <description>", Description: "Set a goal for the current channel."},
217
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "goals", Usage: "GOALS", Description: "List all active goals."},
218
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "done", Usage: "DONE <goal-id>", Description: "Mark a goal as completed."},
219
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "status", Usage: "STATUS", Description: "Report progress on current goals (LLM-enhanced)."},
220
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "assign", Usage: "ASSIGN <nick> <task>", Description: "Manually assign a task to an agent."},
221
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "checkin", Usage: "CHECKIN", Description: "Trigger a check-in round with assigned agents."},
222
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
{Command: "plan", Usage: "PLAN", Description: "Generate a work plan from goals using LLM."},
223
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
},
224
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
225
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
226
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// PolicyStore persists Policies to a JSON file or database.
227
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
type PolicyStore struct {
228
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
mu sync.RWMutex
229
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
path string
230
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
data Policies
231
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defaultBridgeTTLMinutes int
232
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
onChange func(Policies)
233
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
db *store.Store // when non-nil, supersedes path
234
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
235
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
236
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func NewPolicyStore(path string, defaultBridgeTTLMinutes int) (*PolicyStore, error) {
237
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if defaultBridgeTTLMinutes <= 0 {
238
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defaultBridgeTTLMinutes = 5
239
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
240
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps := &PolicyStore{
241
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
path: path,
242
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defaultBridgeTTLMinutes: defaultBridgeTTLMinutes,
243
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
244
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Behaviors = defaultBehaviors
245
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Bridge.WebUserTTLMinutes = defaultBridgeTTLMinutes
246
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := ps.load(); err != nil {
247
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil, err
248
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
249
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return ps, nil
250
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
251
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
252
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) load() error {
253
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
raw, err := os.ReadFile(ps.path)
254
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if os.IsNotExist(err) {
255
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil
256
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
257
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err != nil {
258
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return fmt.Errorf("policies: read %s: %w", ps.path, err)
259
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
260
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return ps.applyRaw(raw)
261
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
262
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
263
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// SetStore switches the policy store to database-backed persistence. The
264
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// current in-memory defaults are merged with any saved policies in the store.
265
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) SetStore(db *store.Store) error {
266
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
raw, err := db.PolicyGet()
267
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err != nil {
268
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return fmt.Errorf("policies: load from db: %w", err)
269
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
270
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.mu.Lock()
271
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defer ps.mu.Unlock()
272
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.db = db
273
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if raw == nil {
274
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil // no saved policies yet; keep defaults
275
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
276
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return ps.applyRaw(raw)
277
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
278
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
279
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// applyRaw merges a JSON blob into the in-memory policy state.
280
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Caller must hold ps.mu if called after initialisation.
281
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) applyRaw(raw []byte) error {
282
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
var p Policies
283
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := json.Unmarshal(raw, &p); err != nil {
284
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return fmt.Errorf("policies: parse: %w", err)
285
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
286
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.normalize(&p)
287
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge saved behaviors over defaults so new built-ins appear automatically.
288
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
saved := make(map[string]BehaviorConfig, len(p.Behaviors))
289
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
for _, b := range p.Behaviors {
290
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
saved[b.ID] = b
291
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
292
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
for i, def := range ps.data.Behaviors {
293
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if sv, ok := saved[def.ID]; ok {
294
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Behaviors[i] = sv
295
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
296
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
297
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.AgentPolicy = p.AgentPolicy
298
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Bridge = p.Bridge
299
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Logging = p.Logging
300
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.LLMBackends = p.LLMBackends
301
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.ROETemplates = p.ROETemplates
302
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.OnJoinMessages = p.OnJoinMessages
303
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil
304
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
305
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
306
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) save() error {
307
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
raw, err := json.MarshalIndent(ps.data, "", " ")
308
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err != nil {
309
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return err
310
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
311
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if ps.db != nil {
312
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return ps.db.PolicySet(raw)
313
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
314
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return os.WriteFile(ps.path, raw, 0600)
315
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
316
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
317
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) Get() Policies {
318
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.mu.RLock()
319
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defer ps.mu.RUnlock()
320
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return ps.data
321
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
322
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
323
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// OnChange registers a callback invoked (in a new goroutine) after each
324
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// successful Set(). The callback receives the new Policies snapshot.
325
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) OnChange(fn func(Policies)) {
326
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.mu.Lock()
327
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defer ps.mu.Unlock()
328
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.onChange = fn
329
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
330
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
331
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) Set(p Policies) error {
332
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.mu.Lock()
333
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defer ps.mu.Unlock()
334
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.normalize(&p)
335
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data = p
336
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := ps.save(); err != nil {
337
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return err
338
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
339
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if ps.onChange != nil {
340
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
snap := ps.data
341
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
fn := ps.onChange
342
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
go fn(snap)
343
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
344
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil
345
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
346
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
347
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge applies a partial Policies update over the current state. Only
348
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// non-zero fields in the patch overwrite existing values. Behaviors are
349
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// merged by ID — existing behaviors keep their defaults for fields not
350
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// present in the patch.
351
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) Merge(patch Policies) error {
352
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.mu.Lock()
353
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
defer ps.mu.Unlock()
354
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
355
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if len(patch.Behaviors) > 0 {
356
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
incoming := make(map[string]BehaviorConfig, len(patch.Behaviors))
357
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
for _, b := range patch.Behaviors {
358
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
incoming[b.ID] = b
359
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
360
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
for i, existing := range ps.data.Behaviors {
361
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched, ok := incoming[existing.ID]; ok {
362
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge: keep existing defaults, overlay patch fields.
363
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.Name != "" {
364
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.Name = patched.Name
365
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
366
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.Description != "" {
367
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.Description = patched.Description
368
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
369
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.Nick != "" {
370
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.Nick = patched.Nick
371
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
372
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.Enabled = patched.Enabled
373
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.JoinAllChannels = patched.JoinAllChannels
374
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.ExcludeChannels != nil {
375
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.ExcludeChannels = patched.ExcludeChannels
376
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
377
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.RequiredChannels != nil {
378
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.RequiredChannels = patched.RequiredChannels
379
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
380
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patched.Config != nil {
381
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
existing.Config = patched.Config
382
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
383
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Behaviors[i] = existing
384
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
385
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
386
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
387
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
388
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge agent_policy if any field is set.
389
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.AgentPolicy.CheckinChannel != "" || patch.AgentPolicy.RequireCheckin || patch.AgentPolicy.RequiredChannels != nil {
390
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.AgentPolicy.CheckinChannel != "" {
391
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.AgentPolicy.CheckinChannel = patch.AgentPolicy.CheckinChannel
392
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
393
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.AgentPolicy.RequireCheckin = patch.AgentPolicy.RequireCheckin
394
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.AgentPolicy.RequiredChannels != nil {
395
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.AgentPolicy.RequiredChannels = patch.AgentPolicy.RequiredChannels
396
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
397
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
398
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
399
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge bridge if set.
400
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.Bridge.WebUserTTLMinutes > 0 {
401
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Bridge.WebUserTTLMinutes = patch.Bridge.WebUserTTLMinutes
402
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
403
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
404
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge logging if any field is set.
405
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.Logging.Dir != "" || patch.Logging.Enabled {
406
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.Logging = patch.Logging
407
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
408
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
409
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge LLM backends if provided.
410
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.LLMBackends != nil {
411
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.LLMBackends = patch.LLMBackends
412
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
413
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
414
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge ROE templates if provided.
415
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.ROETemplates != nil {
416
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.ROETemplates = patch.ROETemplates
417
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
418
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
419
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// Merge on-join messages if provided.
420
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if patch.OnJoinMessages != nil {
421
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.data.OnJoinMessages = patch.OnJoinMessages
422
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
423
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
424
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
ps.normalize(&ps.data)
425
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := ps.save(); err != nil {
426
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return err
427
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
428
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if ps.onChange != nil {
429
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
snap := ps.data
430
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
fn := ps.onChange
431
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
go fn(snap)
432
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
433
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return nil
434
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
435
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
436
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (ps *PolicyStore) normalize(p *Policies) {
437
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if p.Bridge.WebUserTTLMinutes <= 0 {
438
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
p.Bridge.WebUserTTLMinutes = ps.defaultBridgeTTLMinutes
439
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
440
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
441
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
442
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
// --- HTTP handlers ---
443
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
444
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (s *Server) handleGetPolicies(w http.ResponseWriter, r *http.Request) {
445
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeJSON(w, http.StatusOK, s.policies.Get())
446
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
447
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
448
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (s *Server) handlePutPolicies(w http.ResponseWriter, r *http.Request) {
449
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
var p Policies
450
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
451
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeError(w, http.StatusBadRequest, "invalid request body")
452
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return
453
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
454
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := s.policies.Set(p); err != nil {
455
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
s.log.Error("save policies", "err", err)
456
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeError(w, http.StatusInternalServerError, "save failed")
457
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return
458
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
459
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeJSON(w, http.StatusOK, s.policies.Get())
460
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
461
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
462
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
func (s *Server) handlePatchPolicies(w http.ResponseWriter, r *http.Request) {
463
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
var patch Policies
464
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := json.NewDecoder(r.Body).Decode(&patch); err != nil {
465
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeError(w, http.StatusBadRequest, "invalid request body")
466
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return
467
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
468
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
if err := s.policies.Merge(patch); err != nil {
469
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
s.log.Error("merge policies", "err", err)
470
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeError(w, http.StatusInternalServerError, "save failed")
471
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
return
472
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
473
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
writeJSON(w, http.StatusOK, s.policies.Get())
474
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!
}
475
{ copied = false; pop = false }, 1000)" :class="copied && 'copied'">
Copy link Copied!