Files
tutti/services/tuttid/service/agent/activity_projection_runtime_node.go
T
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

120 lines
4.0 KiB
Go

package agent
import (
"context"
"strings"
"time"
agentactivitybiz "github.com/tutti-os/tutti/packages/agent/store-sqlite"
"github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
"github.com/tutti-os/tutti/services/tuttid/biz/agentanalytics"
agentnoderesult "github.com/tutti-os/tutti/services/tuttid/service/reporter/events/agent/node_result"
)
func canonicalRailSection(placement *canonical.RailPlacement) *agentactivitybiz.RailSection {
if placement == nil {
return nil
}
return &agentactivitybiz.RailSection{
Kind: strings.TrimSpace(placement.Kind),
ProjectPath: strings.TrimSpace(placement.ProjectPath),
Key: strings.TrimSpace(placement.SectionKey),
}
}
func (p *ActivityProjection) reportFailedRuntimeNodeResult(ctx context.Context, input canonical.ReportSessionStateInput) {
if p == nil || p.analyticsReporter == nil {
return
}
if !isFailedAgentLifecycleStatus(input.State.LifecycleStatus) {
return
}
errorMessage := strings.TrimSpace(input.State.LastError)
if errorMessage == "" {
errorMessage = "Agent runtime session failed."
}
agentnoderesult.Track(ctx, p.analyticsReporter, agentnoderesult.BuildParams(agentnoderesult.NodeResultInput{
AgentSessionID: input.AgentSessionID,
ErrorCode: classifyRuntimeNodeErrorCode(errorMessage),
ErrorMessage: errorMessage,
Flow: "runtime_activity",
Node: "runtime_exec",
Provider: firstNonEmptyString(input.State.Provider, input.Source.Provider),
Status: "failure",
}))
}
func sessionStateTitle(state canonical.WorkspaceAgentSessionStateUpdate) string {
return firstNonEmptyString(
state.Title,
payloadString(state.RuntimeContext, "title"),
)
}
func activitySessionUpdateEventPayload(workspaceID string, agentSessionID string, lastEventUnixMS int64, agentTargetID ...string) map[string]any {
if lastEventUnixMS <= 0 {
lastEventUnixMS = time.Now().UnixMilli()
}
payload := map[string]any{
"agentSessionId": strings.TrimSpace(agentSessionID),
"eventType": "session_reconcile_required",
"lastEventUnixMs": lastEventUnixMS,
"workspaceId": strings.TrimSpace(workspaceID),
}
if len(agentTargetID) > 0 {
if value := strings.TrimSpace(agentTargetID[0]); value != "" {
payload["agentTargetId"] = value
}
}
return payload
}
func activitySessionDeletedEventPayload(workspaceID string, agentSessionID string) map[string]any {
return map[string]any{
"agentSessionId": strings.TrimSpace(agentSessionID),
"deletedAtUnixMs": time.Now().UnixMilli(),
"eventType": "session_deleted",
"workspaceId": strings.TrimSpace(workspaceID),
}
}
func activitySessionRestoredEventPayload(workspaceID string, agentSessionID string) map[string]any {
return map[string]any{
"agentSessionId": strings.TrimSpace(agentSessionID),
"eventType": "session_restored",
"restoredAtUnixMs": time.Now().UnixMilli(),
"workspaceId": strings.TrimSpace(workspaceID),
}
}
func (p *ActivityProjection) PublishSessionDeleted(ctx context.Context, workspaceID string, agentSessionID string) {
p.publishActivityUpdated(ctx, workspaceID, agentSessionID,
"session_deleted", activitySessionDeletedEventPayload(workspaceID, agentSessionID))
}
func isFailedAgentLifecycleStatus(status string) bool {
switch strings.ToLower(strings.TrimSpace(status)) {
case "failed", "failure", "error", "errored":
return true
default:
return false
}
}
func classifyRuntimeNodeErrorCode(message string) string {
normalized := strings.ToLower(strings.TrimSpace(message))
if strings.Contains(normalized, "network") ||
strings.Contains(normalized, "connection") ||
strings.Contains(normalized, "disconnected") ||
strings.Contains(normalized, "econnreset") ||
strings.Contains(normalized, "socket") {
return agentanalytics.ErrorCodeRuntimeNetworkDisconnected
}
if strings.Contains(normalized, "process") ||
strings.Contains(normalized, "exit") ||
strings.Contains(normalized, "exited") {
return agentanalytics.ErrorCodeRuntimeProcessExited
}
return agentanalytics.ErrorCodeRuntimeExecFailed
}