Files
Memoh/internal/agent/types.go
T
Chrys 33e18e7e64 feat(skills): add effective skill resolution and actions (#377)
* feat(skills): add effective skill resolution and actions

* refactor(workspace): normalize skill-related env and prompt

* chore(api): regenerate skills OpenAPI and SDK artifacts

* feat(web): surface effective skill state in console

* test(skills): cover API and runtime effective state

* fix(web): show adopt action for discovered skills

* fix(web): align skill header and show stateful visibility icon

* refactor(web): compact skill metadata on narrow layouts

* fix(web): constrain long skill text in cards

* refactor(skills): narrow default discovery roots

* fix(skills): harden managed skill path validation

* feat: add path in the results of `use_skill`

---------

Co-authored-by: Acbox <acbox0328@gmail.com>
2026-04-16 13:50:39 +08:00

172 lines
5.2 KiB
Go

package agent
import (
"encoding/json"
"net/http"
"time"
sdk "github.com/memohai/twilight-ai/sdk"
"github.com/memohai/memoh/internal/agent/background"
)
// SessionContext carries request-scoped identity and routing information.
type SessionContext struct {
BotID string
ChatID string
SessionID string
ChannelIdentityID string
CurrentPlatform string
ReplyTarget string
ConversationType string
Timezone string
TimezoneLocation *time.Location
SessionToken string //nolint:gosec // carries session credential material at runtime
IsSubagent bool
}
// SkillEntry represents a skill loaded from the bot container.
type SkillEntry struct {
Name string
Description string
Content string
Path string
Metadata map[string]any
}
// Schedule represents a scheduled task definition.
type Schedule struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Pattern string `json:"pattern"`
MaxCalls *int `json:"maxCalls,omitempty"`
Command string `json:"command"`
}
// LoopDetectionConfig controls loop detection behavior.
type LoopDetectionConfig struct {
Enabled bool
}
// InjectMessage carries a user message to be injected into a running agent
// stream between tool rounds via the PrepareStep hook.
type InjectMessage struct {
Text string
HeaderifiedText string
// ImageParts carries inline images (data URL or public URL) to attach
// alongside the injected text when the model supports vision input.
ImageParts []sdk.ImagePart
}
// RunConfig holds everything needed for a single agent invocation.
type RunConfig struct {
Model *sdk.Model
ReasoningEffort string
Messages []sdk.Message
Query string
System string
SessionType string
SupportsImageInput bool
SupportsToolCall bool
InlineImages []sdk.ImagePart
Identity SessionContext
Skills []SkillEntry
LoopDetection LoopDetectionConfig
Retry RetryConfig
// MidTaskPruneThreshold is the minimum number of messages before mid-task
// pruning kicks in. When the accumulated message count reaches this
// threshold, older tool-result pairs are pruned to keep the context
// within budget. Defaults to MidTaskPruneThresholdDefault (20).
MidTaskPruneThreshold int
// MidTaskPruneKeepSteps is the number of recent tool-call cycles to
// preserve when mid-task pruning is triggered. Defaults to
// MidTaskPruneKeepStepsDefault (4).
MidTaskPruneKeepSteps int
// InjectCh receives user messages to inject between tool rounds.
// When non-nil, a PrepareStep hook drains this channel and appends
// user messages to the conversation before the next LLM call.
InjectCh <-chan InjectMessage
// InjectedRecorder is called each time a message is injected via
// PrepareStep, recording the headerified text and the number of SDK
// output messages that preceded the injection. Used by the resolver
// to interleave injected messages at the correct position in storeRound.
InjectedRecorder func(headerifiedText string, insertAfter int)
// BackgroundManager provides access to the background task system.
// When non-nil, the agent loop drains pending notifications at step
// boundaries and injects them as user messages so the model learns
// about completed background work.
BackgroundManager *background.Manager
}
// GenerateResult holds the result of a non-streaming agent invocation.
type GenerateResult struct {
Messages []sdk.Message
Text string
Attachments []FileAttachment
Reactions []ReactionItem
Speeches []SpeechItem
Usage *sdk.Usage
}
// FileAttachment represents a file reference extracted from agent output.
type FileAttachment struct {
Type string `json:"type"`
Path string `json:"path,omitempty"`
URL string `json:"url,omitempty"`
Mime string `json:"mime,omitempty"`
Name string `json:"name,omitempty"`
ContentHash string `json:"content_hash,omitempty"`
Size int64 `json:"size,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
// ReactionItem represents an emoji reaction extracted from agent output.
type ReactionItem struct {
Emoji string `json:"emoji"`
}
// SpeechItem represents a TTS request extracted from agent output.
type SpeechItem struct {
Text string `json:"text"`
}
// SystemFile is a file loaded from the bot container for prompt generation.
type SystemFile struct {
Filename string
Content string
}
// ModelConfig holds provider and model information resolved from DB.
type ModelConfig struct {
ModelID string
ClientType string
APIKey string //nolint:gosec // carries provider credential material at runtime
CodexAccountID string
BaseURL string
HTTPClient *http.Client
ReasoningConfig *ReasoningConfig
}
// ReasoningConfig controls extended thinking/reasoning behavior.
type ReasoningConfig struct {
Enabled bool
Effort string
}
func mustMarshal(v any) json.RawMessage {
data, err := json.Marshal(v)
if err != nil {
return nil
}
return data
}
// TimeNow is a hook for testing. Defaults to time.Now.
var TimeNow = time.Now