Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
143 lines
4.8 KiB
Go
143 lines
4.8 KiB
Go
package agenthost
|
|
|
|
import (
|
|
"strings"
|
|
|
|
storesqlite "github.com/tutti-os/tutti/packages/agent/store-sqlite"
|
|
)
|
|
|
|
func normalizeOptionalPromptContent(content []PromptContentBlock) ([]PromptContentBlock, string, error) {
|
|
if len(content) == 0 {
|
|
return nil, "", nil
|
|
}
|
|
return normalizePromptContent(content)
|
|
}
|
|
|
|
func createPreparationInput(workspaceID string, input CreateSessionInput) RuntimePreparationInput {
|
|
return RuntimePreparationInput{
|
|
WorkspaceID: workspaceID, AgentSessionID: input.AgentSessionID, AgentTargetID: input.AgentTargetID,
|
|
Provider: input.Provider, Cwd: value(input.Cwd), Title: value(input.Title), PermissionModeID: value(input.PermissionModeID),
|
|
PlanMode: valueBool(input.PlanMode), BrowserUse: valueBoolDefault(input.BrowserUse, true), ComputerUse: valueBoolDefault(input.ComputerUse, true), CodexSaverMode: valueBool(input.CodexSaverMode), RTKSaverMode: valueBool(input.RTKSaverMode),
|
|
ProviderTargetRef: cloneMap(input.ProviderTargetRef), Model: value(input.Model), ReasoningEffort: value(input.ReasoningEffort),
|
|
ConversationDetailMode: input.ConversationDetailMode, Metadata: cloneMap(input.Metadata), RuntimeContext: cloneMap(input.RuntimeContext),
|
|
}
|
|
}
|
|
|
|
func resumePreparationInput(session storesqlite.Session, settings ComposerSettings) RuntimePreparationInput {
|
|
return RuntimePreparationInput{
|
|
WorkspaceID: session.WorkspaceID, AgentSessionID: session.ID, AgentTargetID: session.AgentTargetID,
|
|
Provider: session.Provider, Cwd: session.Cwd, Title: session.Title, PermissionModeID: settings.PermissionModeID,
|
|
PlanMode: settings.PlanMode, BrowserUse: valueBoolDefault(settings.BrowserUse, true), ComputerUse: valueBoolDefault(settings.ComputerUse, true), CodexSaverMode: settings.CodexSaverMode, RTKSaverMode: settings.RTKSaverMode,
|
|
Model: settings.Model, ReasoningEffort: settings.ReasoningEffort, ConversationDetailMode: settings.ConversationDetailMode,
|
|
RuntimeContext: cloneMap(session.InternalRuntimeContext), SessionOrigin: session.Origin,
|
|
ProviderSessionID: session.ProviderSessionID, CreatedAtUnixMS: session.CreatedAtUnixMS,
|
|
UpdatedAtUnixMS: session.UpdatedAtUnixMS, Visible: session.Metadata.Visible, Settings: settings,
|
|
SessionMetadata: session.Metadata,
|
|
}
|
|
}
|
|
|
|
func overlayRuntimeContext(base map[string]any, overlay map[string]any) map[string]any {
|
|
result := cloneMap(base)
|
|
if len(overlay) == 0 {
|
|
return result
|
|
}
|
|
if result == nil {
|
|
result = make(map[string]any, len(overlay))
|
|
}
|
|
for key, value := range cloneMap(overlay) {
|
|
baseMap, baseIsMap := result[key].(map[string]any)
|
|
overlayMap, overlayIsMap := value.(map[string]any)
|
|
if baseIsMap && overlayIsMap {
|
|
result[key] = overlayRuntimeContext(baseMap, overlayMap)
|
|
continue
|
|
}
|
|
result[key] = value
|
|
}
|
|
return result
|
|
}
|
|
|
|
func composerSettingsFromMap(values map[string]any) ComposerSettings {
|
|
result := ComposerSettings{}
|
|
result.CodexSaverMode, _ = values["codexSaverMode"].(bool)
|
|
result.RTKSaverMode, _ = values["rtkSaverMode"].(bool)
|
|
result.Model, _ = values["model"].(string)
|
|
result.PermissionModeID, _ = values["permissionModeId"].(string)
|
|
result.PlanMode, _ = values["planMode"].(bool)
|
|
if value, ok := values["browserUse"].(bool); ok {
|
|
result.BrowserUse = &value
|
|
}
|
|
if value, ok := values["computerUse"].(bool); ok {
|
|
result.ComputerUse = &value
|
|
}
|
|
result.ReasoningEffort, _ = values["reasoningEffort"].(string)
|
|
result.Speed, _ = values["speed"].(string)
|
|
result.ConversationDetailMode, _ = values["conversationDetailMode"].(string)
|
|
return result
|
|
}
|
|
|
|
func lifecycleFromTurn(turn storesqlite.Turn) TurnLifecycle {
|
|
result := TurnLifecycle{Phase: turn.Phase}
|
|
if turnID := strings.TrimSpace(turn.TurnID); turnID != "" && turn.Phase != "settled" {
|
|
result.ActiveTurnID = &turnID
|
|
}
|
|
if turn.Outcome != "" {
|
|
outcome := turn.Outcome
|
|
result.Outcome = &outcome
|
|
}
|
|
if turn.CompletedCommandKind != "" || turn.CompletedCommandStatus != "" {
|
|
result.CompletedCommand = &CompletedCommand{Kind: turn.CompletedCommandKind, Status: turn.CompletedCommandStatus}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func imageOnlyDisplayText(content []PromptContentBlock) string {
|
|
count := 0
|
|
for _, block := range content {
|
|
if block.Type == "image" {
|
|
count++
|
|
}
|
|
}
|
|
if count == 1 {
|
|
return "[Image]"
|
|
}
|
|
if count > 1 {
|
|
return "[Images]"
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func value(input *string) string {
|
|
if input == nil {
|
|
return ""
|
|
}
|
|
return strings.TrimSpace(*input)
|
|
}
|
|
|
|
func valueBool(input *bool) bool { return input != nil && *input }
|
|
|
|
func valueBoolDefault(input *bool, fallback bool) bool {
|
|
if input == nil {
|
|
return fallback
|
|
}
|
|
return *input
|
|
}
|
|
|
|
func boolPointer(value bool) *bool { return &value }
|
|
|
|
func cloneStringPointer(value *string) *string {
|
|
if value == nil {
|
|
return nil
|
|
}
|
|
cloned := *value
|
|
return &cloned
|
|
}
|
|
|
|
func firstMap(values ...map[string]any) map[string]any {
|
|
for _, value := range values {
|
|
if len(value) > 0 {
|
|
return value
|
|
}
|
|
}
|
|
return nil
|
|
}
|