Merge remote-tracking branch 'origin/main' into fix/heartbeat-completion-message

This commit is contained in:
dj-oyu 2026-03-02 03:58:45 +09:00
commit 0b3b99535d
9 changed files with 60 additions and 64 deletions

View file

@ -255,7 +255,6 @@ func (c *DiscordChannel) SendWithID(ctx context.Context, chatID string, content
} }
} }
// EditMessage implements channels.MessageEditor. // EditMessage implements channels.MessageEditor.
func (c *DiscordChannel) EditMessage(ctx context.Context, chatID string, messageID string, content string) error { func (c *DiscordChannel) EditMessage(ctx context.Context, chatID string, messageID string, content string) error {
_, err := c.session.ChannelMessageEdit(chatID, messageID, content) _, err := c.session.ChannelMessageEdit(chatID, messageID, content)

View file

@ -28,7 +28,6 @@ type MessageSenderWithID interface {
SendWithID(ctx context.Context, chatID string, content string) (messageID string, err error) SendWithID(ctx context.Context, chatID string, content string) (messageID string, err error)
} }
// PlaceholderCapable — channels that can send a placeholder message // PlaceholderCapable — channels that can send a placeholder message
// (e.g. "Thinking... 💭") that will later be edited to the actual response. // (e.g. "Thinking... 💭") that will later be edited to the actual response.
// The channel MUST also implement MessageEditor for the placeholder to be useful. // The channel MUST also implement MessageEditor for the placeholder to be useful.

View file

@ -63,7 +63,6 @@ type statusMsgEntry struct {
createdAt time.Time createdAt time.Time
} }
// channelRateConfig maps channel name to per-second rate limit. // channelRateConfig maps channel name to per-second rate limit.
var channelRateConfig = map[string]float64{ var channelRateConfig = map[string]float64{
"telegram": 20, "telegram": 20,
@ -559,7 +558,6 @@ func (m *Manager) handleTaskStatusSend(ctx context.Context, name string, w *chan
_ = w.ch.Send(ctx, msg) _ = w.ch.Send(ctx, msg)
} }
// sendWithRetry sends a message through the channel with rate limiting and // sendWithRetry sends a message through the channel with rate limiting and
// retry logic. It classifies errors to determine the retry strategy: // retry logic. It classifies errors to determine the retry strategy:
// - ErrNotRunning / ErrSendFailed: permanent, no retry // - ErrNotRunning / ErrSendFailed: permanent, no retry

View file

@ -170,7 +170,6 @@ func (c *PicoChannel) SendWithID(ctx context.Context, chatID string, content str
return msgID, nil return msgID, nil
} }
// EditMessage implements channels.MessageEditor. // EditMessage implements channels.MessageEditor.
func (c *PicoChannel) EditMessage(ctx context.Context, chatID string, messageID string, content string) error { func (c *PicoChannel) EditMessage(ctx context.Context, chatID string, messageID string, content string) error {
outMsg := newMessage(TypeMessageUpdate, map[string]any{ outMsg := newMessage(TypeMessageUpdate, map[string]any{

View file

@ -318,7 +318,6 @@ func (c *SlackChannel) handleMessageEvent(ev *slackevents.MessageEvent) {
var mediaPaths []string var mediaPaths []string
scope := channels.BuildMediaScope("slack", chatID, messageTS) scope := channels.BuildMediaScope("slack", chatID, messageTS)
// Helper to register a local file with the media store // Helper to register a local file with the media store

View file

@ -221,7 +221,6 @@ func (c *TelegramChannel) SendWithID(ctx context.Context, chatID string, content
return fmt.Sprintf("%d", sent.MessageID), nil return fmt.Sprintf("%d", sent.MessageID), nil
} }
// StartTyping implements channels.TypingCapable. // StartTyping implements channels.TypingCapable.
// It sends ChatAction(typing) immediately and then repeats every 4 seconds // It sends ChatAction(typing) immediately and then repeats every 4 seconds
// (Telegram's typing indicator expires after ~5s) in a background goroutine. // (Telegram's typing indicator expires after ~5s) in a background goroutine.

View file

@ -596,7 +596,17 @@ func normalizeModel(model, apiBase string) string {
prefix := strings.ToLower(before) prefix := strings.ToLower(before)
switch prefix { switch prefix {
case "openai", "moonshot", "nvidia", "groq", "ollama", "deepseek", "google", "openrouter", "zhipu", "minimax", "mistral": case "openai",
"moonshot",
"nvidia",
"groq",
"ollama",
"deepseek",
"google",
"openrouter",
"zhipu",
"minimax",
"mistral":
return after return after
default: default:
return model return model

View file

@ -134,61 +134,55 @@ type ExecTool struct {
bgCtx context.Context bgCtx context.Context
} }
var ( var defaultDenyPatterns = []*regexp.Regexp{
defaultDenyPatterns = []*regexp.Regexp{ regexp.MustCompile(`\brm\s+-[rf]{1,2}\b`),
regexp.MustCompile(`\brm\s+-[rf]{1,2}\b`), regexp.MustCompile(`\bdel\s+/[fq]\b`),
regexp.MustCompile(`\bdel\s+/[fq]\b`), regexp.MustCompile(`\brmdir\s+/s\b`),
regexp.MustCompile(`\brmdir\s+/s\b`), // Match disk wiping commands (must be followed by space/args)
// Match disk wiping commands (must be followed by space/args) regexp.MustCompile(
regexp.MustCompile( `\b(format|mkfs|diskpart)\b\s`,
`\b(format|mkfs|diskpart)\b\s`, ),
), regexp.MustCompile(`\bdd\s+if=`),
regexp.MustCompile(`\bdd\s+if=`), regexp.MustCompile(`>\s*/dev/sd[a-z]\b`), // Block writes to disk devices (but allow /dev/null)
regexp.MustCompile(`>\s*/dev/sd[a-z]\b`), // Block writes to disk devices (but allow /dev/null) regexp.MustCompile(`\b(shutdown|reboot|poweroff)\b`),
regexp.MustCompile(`\b(shutdown|reboot|poweroff)\b`), regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`),
regexp.MustCompile(`:\(\)\s*\{.*\};\s*:`), regexp.MustCompile(`\$\([^)]+\)`),
regexp.MustCompile(`\$\([^)]+\)`), regexp.MustCompile(`\$\{[^}]+\}`),
regexp.MustCompile(`\$\{[^}]+\}`), regexp.MustCompile("`[^`]+`"),
regexp.MustCompile("`[^`]+`"), regexp.MustCompile(`\|\s*sh\b`),
regexp.MustCompile(`\|\s*sh\b`), regexp.MustCompile(`\|\s*bash\b`),
regexp.MustCompile(`\|\s*bash\b`), regexp.MustCompile(`;\s*rm\s+-[rf]`),
regexp.MustCompile(`;\s*rm\s+-[rf]`), regexp.MustCompile(`&&\s*rm\s+-[rf]`),
regexp.MustCompile(`&&\s*rm\s+-[rf]`), regexp.MustCompile(`\|\|\s*rm\s+-[rf]`),
regexp.MustCompile(`\|\|\s*rm\s+-[rf]`), regexp.MustCompile(`>\s*/dev/null\s*>&?\s*\d?`),
regexp.MustCompile(`>\s*/dev/null\s*>&?\s*\d?`), regexp.MustCompile(`<<\s*EOF`),
regexp.MustCompile(`<<\s*EOF`), regexp.MustCompile(`\$\(\s*cat\s+`),
regexp.MustCompile(`\$\(\s*cat\s+`), regexp.MustCompile(`\$\(\s*curl\s+`),
regexp.MustCompile(`\$\(\s*curl\s+`), regexp.MustCompile(`\$\(\s*wget\s+`),
regexp.MustCompile(`\$\(\s*wget\s+`), regexp.MustCompile(`\$\(\s*which\s+`),
regexp.MustCompile(`\$\(\s*which\s+`), regexp.MustCompile(`\bsudo\b`),
regexp.MustCompile(`\bsudo\b`), regexp.MustCompile(`\bchmod\s+[0-7]{3,4}\b`),
regexp.MustCompile(`\bchmod\s+[0-7]{3,4}\b`), regexp.MustCompile(`\bchown\b`),
regexp.MustCompile(`\bchown\b`), regexp.MustCompile(`\bpkill\b`),
regexp.MustCompile(`\bpkill\b`), regexp.MustCompile(`\bkillall\b`),
regexp.MustCompile(`\bkillall\b`), regexp.MustCompile(`\bkill\s+-[9]\b`),
regexp.MustCompile(`\bkill\s+-[9]\b`), regexp.MustCompile(`\bcurl\b.*\|\s*(sh|bash)`),
regexp.MustCompile(`\bcurl\b.*\|\s*(sh|bash)`), regexp.MustCompile(`\bwget\b.*\|\s*(sh|bash)`),
regexp.MustCompile(`\bwget\b.*\|\s*(sh|bash)`), regexp.MustCompile(`\bnpm\s+install\s+-g\b`),
regexp.MustCompile(`\bnpm\s+install\s+-g\b`), regexp.MustCompile(`\bpip\s+install\s+--user\b`),
regexp.MustCompile(`\bpip\s+install\s+--user\b`), regexp.MustCompile(`\bapt\s+(install|remove|purge)\b`),
regexp.MustCompile(`\bapt\s+(install|remove|purge)\b`), regexp.MustCompile(`\byum\s+(install|remove)\b`),
regexp.MustCompile(`\byum\s+(install|remove)\b`), regexp.MustCompile(`\bdnf\s+(install|remove)\b`),
regexp.MustCompile(`\bdnf\s+(install|remove)\b`), regexp.MustCompile(`\bdocker\s+run\b`),
regexp.MustCompile(`\bdocker\s+run\b`), regexp.MustCompile(`\bdocker\s+exec\b`),
regexp.MustCompile(`\bdocker\s+exec\b`), regexp.MustCompile(`\bgit\s+push\b`),
regexp.MustCompile(`\bgit\s+push\b`), regexp.MustCompile(`\bgit\s+force\b`),
regexp.MustCompile(`\bgit\s+force\b`), regexp.MustCompile(`\bgit\s+checkout\b`),
regexp.MustCompile(`\bgit\s+checkout\b`), regexp.MustCompile(`\bgit\s+switch\b`),
regexp.MustCompile(`\bgit\s+switch\b`), regexp.MustCompile(`\bssh\b.*@`),
regexp.MustCompile(`\bssh\b.*@`), regexp.MustCompile(`\beval\b`),
regexp.MustCompile(`\beval\b`), regexp.MustCompile(`\bsource\s+.*\.sh\b`),
regexp.MustCompile(`\bsource\s+.*\.sh\b`), }
}
// absolutePathPattern matches absolute file paths in commands (Unix and Windows).
absolutePathPattern = regexp.MustCompile(`[A-Za-z]:\\[^\\\"']+|/[^\s\"']+`)
)
func NewExecTool(workingDir string, restrict bool) (*ExecTool, error) { func NewExecTool(workingDir string, restrict bool) (*ExecTool, error) {
return NewExecToolWithConfig(workingDir, restrict, nil) return NewExecToolWithConfig(workingDir, restrict, nil)

View file

@ -95,7 +95,6 @@ func DetectRepetitionLoop(text string) bool {
return ratio < repetitionUniqueThreshold return ratio < repetitionUniqueThreshold
} }
// SanitizeMessageContent removes Unicode control characters, format characters (RTL overrides, // SanitizeMessageContent removes Unicode control characters, format characters (RTL overrides,
// zero-width characters), and other non-graphic characters that could confuse an LLM // zero-width characters), and other non-graphic characters that could confuse an LLM
// or cause display issues in the agent UI. // or cause display issues in the agent UI.