- add BotCommandInfo and CommandRegistrar interface for platform abstraction - implement GetAllCommands to collect builtin/custom commands and skills - add i18n descriptions for all 30 builtin commands (en/zh/zh-TW/ja/es) - implement RegisterCommands for Telegram setMyCommands API integration
244 lines
9.8 KiB
Go
244 lines
9.8 KiB
Go
package core
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
)
|
|
|
|
// Platform abstracts a messaging platform (Feishu, DingTalk, Slack, etc.).
|
|
type Platform interface {
|
|
Name() string
|
|
Start(handler MessageHandler) error
|
|
Reply(ctx context.Context, replyCtx any, content string) error
|
|
Send(ctx context.Context, replyCtx any, content string) error
|
|
Stop() error
|
|
}
|
|
|
|
// ErrNotSupported indicates a platform doesn't support a particular operation.
|
|
var ErrNotSupported = errors.New("operation not supported by this platform")
|
|
|
|
// ReplyContextReconstructor is an optional interface for platforms that can
|
|
// recreate a reply context from a session key. This is needed for cron jobs
|
|
// to send messages to users without an incoming message.
|
|
type ReplyContextReconstructor interface {
|
|
ReconstructReplyCtx(sessionKey string) (any, error)
|
|
}
|
|
|
|
// SessionEnvInjector is an optional interface for agents that accept
|
|
// per-session environment variables (e.g. CC_PROJECT, CC_SESSION_KEY).
|
|
type SessionEnvInjector interface {
|
|
SetSessionEnv(env []string)
|
|
}
|
|
|
|
// AgentSystemPrompt returns the system prompt fragment that informs agents about
|
|
// cc-connect capabilities (cron scheduling, etc.).
|
|
// The prompt is designed to be appended to the agent's existing system prompt.
|
|
func AgentSystemPrompt() string {
|
|
return `You are running inside cc-connect, a bridge that connects you to messaging platforms.
|
|
Your responses are automatically delivered to the user — just reply normally, do NOT use cc-connect send.
|
|
|
|
## Available tools
|
|
|
|
### Scheduled tasks (cron)
|
|
When the user asks you to do something on a schedule (e.g. "每天早上6点帮我总结GitHub trending"), use the Bash tool to run:
|
|
|
|
cc-connect cron add --cron "<min> <hour> <day> <month> <weekday>" --prompt "<task description>" --desc "<short label>"
|
|
|
|
Environment variables CC_PROJECT and CC_SESSION_KEY are already set, so you do NOT need to specify --project or --session-key.
|
|
|
|
Examples:
|
|
cc-connect cron add --cron "0 6 * * *" --prompt "Collect GitHub trending repos and send a summary" --desc "Daily GitHub Trending"
|
|
cc-connect cron add --cron "0 9 * * 1" --prompt "Generate a weekly project status report" --desc "Weekly Report"
|
|
|
|
You can also list or delete cron jobs:
|
|
cc-connect cron list
|
|
cc-connect cron del <job-id>
|
|
|
|
### Bot-to-bot relay
|
|
When you need to communicate with another bot (e.g. ask another AI agent a question), use:
|
|
|
|
cc-connect relay send --to <target_project> "<message>"
|
|
|
|
IMPORTANT: <target_project> must be the EXACT project name from the /bind command output.
|
|
Do NOT guess or modify the name — use it exactly as shown (e.g. "gemini", not "gemini-bot").
|
|
|
|
This sends a message to the target bot and waits for its response (printed to stdout).
|
|
The conversation is visible in the group chat and each bot maintains its own relay session.
|
|
|
|
Environment variables CC_PROJECT and CC_SESSION_KEY are already set, so the relay knows which group chat to use.
|
|
`
|
|
}
|
|
|
|
// TypingIndicator is an optional interface for platforms that can show a
|
|
// "processing" indicator (typing bubble, emoji reaction, etc.) while the
|
|
// agent is working. StartTyping is called when processing begins and returns
|
|
// a stop function that the caller must invoke when processing ends.
|
|
type TypingIndicator interface {
|
|
StartTyping(ctx context.Context, replyCtx any) (stop func())
|
|
}
|
|
|
|
// MessageUpdater is an optional interface for platforms that support updating messages.
|
|
type MessageUpdater interface {
|
|
UpdateMessage(ctx context.Context, replyCtx any, content string) error
|
|
}
|
|
|
|
// ButtonOption represents a clickable inline button.
|
|
type ButtonOption struct {
|
|
Text string // display text on the button
|
|
Data string // callback data returned when clicked (≤64 bytes for Telegram)
|
|
}
|
|
|
|
// InlineButtonSender is an optional interface for platforms that support
|
|
// sending messages with clickable inline buttons (e.g. Telegram Inline Keyboard).
|
|
// Buttons is a 2D slice: each inner slice is one row of buttons.
|
|
type InlineButtonSender interface {
|
|
SendWithButtons(ctx context.Context, replyCtx any, content string, buttons [][]ButtonOption) error
|
|
}
|
|
|
|
// MessageHandler is called by platforms when a new message arrives.
|
|
type MessageHandler func(p Platform, msg *Message)
|
|
|
|
// Agent abstracts an AI coding assistant (Claude Code, Cursor, Gemini CLI, etc.).
|
|
// All agents must support persistent bidirectional sessions via StartSession.
|
|
type Agent interface {
|
|
Name() string
|
|
// StartSession creates or resumes an interactive session with a persistent process.
|
|
StartSession(ctx context.Context, sessionID string) (AgentSession, error)
|
|
// ListSessions returns sessions known to the agent backend.
|
|
ListSessions(ctx context.Context) ([]AgentSessionInfo, error)
|
|
Stop() error
|
|
}
|
|
|
|
// AgentSession represents a running interactive agent session with a persistent process.
|
|
type AgentSession interface {
|
|
// Send sends a user message (with optional images) to the running agent process.
|
|
Send(prompt string, images []ImageAttachment) error
|
|
// RespondPermission sends a permission decision back to the agent process.
|
|
RespondPermission(requestID string, result PermissionResult) error
|
|
// Events returns the channel that emits agent events (kept open across turns).
|
|
Events() <-chan Event
|
|
// CurrentSessionID returns the current agent-side session ID.
|
|
CurrentSessionID() string
|
|
// Alive returns true if the underlying process is still running.
|
|
Alive() bool
|
|
// Close terminates the session and its underlying process.
|
|
Close() error
|
|
}
|
|
|
|
// PermissionResult represents the user's decision on a permission request.
|
|
type PermissionResult struct {
|
|
Behavior string `json:"behavior"` // "allow" or "deny"
|
|
UpdatedInput map[string]any `json:"updatedInput,omitempty"` // echoed back for allow
|
|
Message string `json:"message,omitempty"` // reason for deny
|
|
}
|
|
|
|
// ToolAuthorizer is an optional interface for agents that support dynamic tool authorization.
|
|
type ToolAuthorizer interface {
|
|
AddAllowedTools(tools ...string) error
|
|
GetAllowedTools() []string
|
|
}
|
|
|
|
// HistoryProvider is an optional interface for agents that can retrieve
|
|
// conversation history from their backend session files.
|
|
type HistoryProvider interface {
|
|
GetSessionHistory(ctx context.Context, sessionID string, limit int) ([]HistoryEntry, error)
|
|
}
|
|
|
|
// ProviderConfig holds API provider settings for an agent.
|
|
type ProviderConfig struct {
|
|
Name string
|
|
APIKey string
|
|
BaseURL string
|
|
Model string
|
|
Thinking string // override thinking type sent to this provider ("disabled", "enabled", or "" for no rewrite)
|
|
Env map[string]string // arbitrary extra env vars (e.g. CLAUDE_CODE_USE_BEDROCK=1)
|
|
}
|
|
|
|
// ProviderSwitcher is an optional interface for agents that support multiple API providers.
|
|
type ProviderSwitcher interface {
|
|
SetProviders(providers []ProviderConfig)
|
|
SetActiveProvider(name string) bool
|
|
GetActiveProvider() *ProviderConfig
|
|
ListProviders() []ProviderConfig
|
|
}
|
|
|
|
// MemoryFileProvider is an optional interface for agents that support
|
|
// persistent instruction files (CLAUDE.md, AGENTS.md, GEMINI.md, etc.).
|
|
// The engine uses these paths for the /memory command.
|
|
type MemoryFileProvider interface {
|
|
ProjectMemoryFile() string // project-level instruction file (e.g., <work_dir>/CLAUDE.md)
|
|
GlobalMemoryFile() string // user-level instruction file (e.g., ~/.claude/CLAUDE.md)
|
|
}
|
|
|
|
// ModelSwitcher is an optional interface for agents that support runtime model switching.
|
|
// Model changes take effect on the next session (existing sessions keep their model).
|
|
type ModelSwitcher interface {
|
|
SetModel(model string)
|
|
GetModel() string
|
|
// AvailableModels tries to fetch models from the provider API.
|
|
// Falls back to a built-in list on failure.
|
|
AvailableModels(ctx context.Context) []ModelOption
|
|
}
|
|
|
|
// ModelOption describes a selectable model.
|
|
type ModelOption struct {
|
|
Name string // model identifier passed to CLI
|
|
Desc string // short description (display_name or empty)
|
|
}
|
|
|
|
// ContextCompressor is an optional interface for agents that support
|
|
// compressing/compacting the conversation context within a running session.
|
|
// CompressCommand returns the native slash command (e.g. "/compact", "/compress")
|
|
// that will be forwarded to the agent process. Return "" if not supported.
|
|
type ContextCompressor interface {
|
|
CompressCommand() string
|
|
}
|
|
|
|
// CommandProvider is an optional interface for agents that expose custom slash
|
|
// commands via local files (e.g. .claude/commands/*.md). The engine scans the
|
|
// returned directories for *.md files and registers them as slash commands.
|
|
type CommandProvider interface {
|
|
CommandDirs() []string
|
|
}
|
|
|
|
// SkillProvider is an optional interface for agents that expose skills via
|
|
// local directories (e.g. .claude/skills/<name>/SKILL.md). Each subdirectory
|
|
// containing a SKILL.md is treated as a skill. Skills are project-level and
|
|
// agent-specific — they are NOT shared across different agent types.
|
|
type SkillProvider interface {
|
|
SkillDirs() []string
|
|
}
|
|
|
|
// SessionDeleter is an optional interface for agents that support deleting sessions.
|
|
type SessionDeleter interface {
|
|
DeleteSession(ctx context.Context, sessionID string) error
|
|
}
|
|
|
|
// ModeSwitcher is an optional interface for agents that support runtime permission mode switching.
|
|
type ModeSwitcher interface {
|
|
SetMode(mode string)
|
|
GetMode() string
|
|
PermissionModes() []PermissionModeInfo
|
|
}
|
|
|
|
// PermissionModeInfo describes a permission mode for display.
|
|
type PermissionModeInfo struct {
|
|
Key string
|
|
Name string
|
|
NameZh string
|
|
Desc string
|
|
DescZh string
|
|
}
|
|
|
|
// BotCommandInfo represents a command for bot menu registration (e.g. Telegram setMyCommands).
|
|
type BotCommandInfo struct {
|
|
Command string // command name without leading "/"
|
|
Description string // short description for the menu
|
|
}
|
|
|
|
// CommandRegistrar is an optional interface for platforms that support
|
|
// registering commands to the platform's native menu (e.g. Telegram's setMyCommands).
|
|
type CommandRegistrar interface {
|
|
RegisterCommands(commands []BotCommandInfo) error
|
|
}
|