Files
tutti/services/tuttid/agent_runtime_adapter.go
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

784 lines
30 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"strings"
agentruntime "github.com/tutti-os/tutti/packages/agent/daemon/runtime"
agenthost "github.com/tutti-os/tutti/packages/agent/host"
agentactivitybiz "github.com/tutti-os/tutti/packages/agent/store-sqlite"
"github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
agentservice "github.com/tutti-os/tutti/services/tuttid/service/agent"
)
type agentRuntimeAdapter struct {
controller *agentruntime.Controller
}
func (a agentRuntimeAdapter) ObserveRootTurnSettled(_ context.Context, workspaceID string, agentSessionID string, turn agentactivitybiz.Turn) {
a.controller.ReconcileRootTurnSettlement(agentruntime.RootTurnSettlement{
RoomID: workspaceID,
AgentSessionID: agentSessionID,
TurnID: turn.TurnID,
Outcome: turn.Outcome,
ErrorMessage: turn.ErrorMessage,
})
}
func newAgentRuntimeAdapter(controller *agentruntime.Controller) agentRuntimeAdapter {
return agentRuntimeAdapter{controller: controller}
}
func (a agentRuntimeAdapter) ConnectorHTTPMCPSupported(
ctx context.Context,
input agentservice.ConnectorCapabilityInput,
) (bool, error) {
capabilities, err := a.controller.ConnectorCapabilities(ctx, agentruntime.ConnectorCapabilityInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
AgentTargetID: input.AgentTargetID,
Provider: input.Provider,
CWD: input.Cwd,
Env: append([]string(nil), input.Env...),
ProviderTargetRef: cloneRuntimeContext(input.ProviderTargetRef),
PermissionModeID: input.PermissionModeID,
Settings: agentRuntimeSessionSettings(input.Settings),
})
return capabilities.HTTPMCP, err
}
func (a agentRuntimeAdapter) Cancel(ctx context.Context, input agentservice.RuntimeCancelInput) (agentservice.RuntimeCancelResult, error) {
targets := make([]agentruntime.CancelTarget, 0, len(input.Targets))
for _, target := range input.Targets {
targets = append(targets, agentruntime.CancelTarget{
AgentSessionID: target.AgentSessionID,
TurnID: target.TurnID,
})
}
result, err := a.controller.Cancel(ctx, agentruntime.CancelInput{
RoomID: input.WorkspaceID,
RootAgentSessionID: input.RootAgentSessionID,
Targets: targets,
Reason: input.Reason,
})
if err != nil {
return agentservice.RuntimeCancelResult{}, mapAgentRuntimeError(err)
}
confirmedTargets := make([]agentservice.RuntimeCancelTarget, 0, len(result.ConfirmedTargets))
for _, target := range result.ConfirmedTargets {
confirmedTargets = append(confirmedTargets, agentservice.RuntimeCancelTarget{
AgentSessionID: target.AgentSessionID,
TurnID: target.TurnID,
})
}
return agentservice.RuntimeCancelResult{
AgentSessionID: result.AgentSessionID,
Canceled: result.Canceled,
TargetAbsent: result.TargetAbsent,
ConfirmedTargets: confirmedTargets,
}, nil
}
func (a agentRuntimeAdapter) GoalControl(ctx context.Context, input agentservice.RuntimeGoalControlInput) (agentservice.RuntimeGoalControlResult, error) {
result, err := a.controller.GoalControl(ctx, agentruntime.GoalControlInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
Action: agentruntime.GoalControlAction(input.Action),
Objective: input.Objective,
OperationID: input.OperationID,
GoalRevision: input.GoalRevision,
RepairEpoch: input.RepairEpoch,
SubmissionMetadata: input.SubmissionMetadata,
RequireLive: input.RequireLive,
})
if err != nil {
return agentservice.RuntimeGoalControlResult{}, mapAgentRuntimeError(err)
}
return agentservice.RuntimeGoalControlResult{
AgentSessionID: result.AgentSessionID,
Goal: result.Goal,
Evidence: result.Evidence,
ProviderPhase: result.ProviderPhase,
}, nil
}
func (a agentRuntimeAdapter) ReconcileGoal(ctx context.Context, input agentservice.RuntimeGoalControlInput) (agentservice.RuntimeGoalReconcileResult, error) {
result, err := a.controller.ReconcileGoal(ctx, agentruntime.GoalReconcileInput{
RoomID: input.WorkspaceID, AgentSessionID: input.AgentSessionID, RequireLive: input.RequireLive,
})
if err != nil {
return agentservice.RuntimeGoalReconcileResult{}, mapAgentRuntimeError(err)
}
return agentservice.RuntimeGoalReconcileResult{
AgentSessionID: result.AgentSessionID, Goal: result.Goal, Evidence: result.Evidence,
}, nil
}
func (a agentRuntimeAdapter) GoalRecoveryPolicy(ctx context.Context, input agentservice.RuntimeGoalControlInput) (agentservice.RuntimeGoalRecoveryPolicy, error) {
capabilities, err := a.controller.GoalCapabilities(ctx, agentruntime.GoalReconcileInput{RoomID: input.WorkspaceID, AgentSessionID: input.AgentSessionID})
if err != nil {
return agentservice.RuntimeGoalRecoveryPolicy{}, mapAgentRuntimeError(err)
}
return agentservice.RuntimeGoalRecoveryPolicy{QuerySupported: capabilities.QuerySupported, ReplaySetAfterRestart: capabilities.ReplaySetAfterRestart}, nil
}
func agentRuntimeSessionSettings(settings agentservice.ComposerSettings) *agentruntime.SessionSettings {
result := &agentruntime.SessionSettings{
Model: settings.Model,
ReasoningEffort: settings.ReasoningEffort,
Speed: settings.Speed,
PlanMode: settings.PlanMode,
PermissionModeID: settings.PermissionModeID,
ConversationDetailMode: settings.ConversationDetailMode,
}
if settings.BrowserUse != nil {
value := *settings.BrowserUse
result.BrowserUse = &value
}
return result
}
func (a agentRuntimeAdapter) CanResume(input agentservice.RuntimeResumeInput) bool {
return a.controller.CanResume(agentruntime.ResumeInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
AgentTargetID: input.AgentTargetID,
Provider: input.Provider,
ProviderSessionID: input.ProviderSessionID,
Resumable: input.Resumable,
CWD: input.Cwd,
Env: append([]string(nil), input.Env...),
MCPServers: daemonMCPServerBindings(input.MCPServers),
Title: input.Title,
Status: input.Status,
Settings: agentRuntimeSessionSettings(input.Settings),
PermissionModeID: input.Settings.PermissionModeID,
CreatedAtUnixMS: input.CreatedAtUnixMS,
UpdatedAtUnixMS: input.UpdatedAtUnixMS,
Visible: input.Visible,
RuntimeContext: cloneRuntimeContext(input.RuntimeContext),
ProviderTargetRef: cloneRuntimeContext(input.ProviderTargetRef),
})
}
func (a agentRuntimeAdapter) Close(ctx context.Context, input agentservice.RuntimeCloseInput) error {
if _, err := a.controller.Close(ctx, agentruntime.CloseInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
PreserveCanonicalState: input.PreserveCanonicalState,
}); err != nil {
return mapAgentRuntimeError(err)
}
return nil
}
func (a agentRuntimeAdapter) DisconnectRuntimeSession(
ctx context.Context,
workspaceID string,
agentSessionID string,
) (bool, error) {
result, err := a.controller.DisconnectRuntimeSession(ctx, workspaceID, agentSessionID)
if err != nil {
return false, mapAgentRuntimeError(err)
}
return result.Disconnected, nil
}
func (a agentRuntimeAdapter) SnapshotWorkspaceRuntimeDisconnectTargets(workspaceID string) []agenthost.RuntimeDisconnectTarget {
targets := a.controller.SnapshotRuntimeDisconnectTargets(workspaceID)
result := make([]agenthost.RuntimeDisconnectTarget, 0, len(targets))
for _, target := range targets {
result = append(result, agenthost.RuntimeDisconnectTarget{
WorkspaceID: target.RoomID, AgentSessionID: target.AgentSessionID,
ConnectionGeneration: target.ConnectionGeneration,
})
}
return result
}
func (a agentRuntimeAdapter) DisconnectRuntimeSessionTarget(
ctx context.Context,
target agenthost.RuntimeDisconnectTarget,
) (bool, error) {
result, err := a.controller.DisconnectRuntimeSessionTarget(ctx, agentruntime.RuntimeDisconnectTarget{
RoomID: target.WorkspaceID, AgentSessionID: target.AgentSessionID,
ConnectionGeneration: target.ConnectionGeneration,
})
if err != nil {
return false, mapAgentRuntimeError(err)
}
return result.Disconnected, nil
}
func (a agentRuntimeAdapter) Exec(ctx context.Context, input agentservice.RuntimeExecInput) (agentservice.RuntimeExecResult, error) {
if !input.Guidance && strings.TrimSpace(input.TurnID) == "" {
return agentservice.RuntimeExecResult{}, fmt.Errorf(
"%w: canonical turn id is required for a new agent turn",
agentservice.ErrInvalidArgument,
)
}
agentservice.LogSubmitTrace("runtime_adapter.exec.entered", input.WorkspaceID, input.AgentSessionID, input.ClientSubmitID, input.Metadata, map[string]any{
"content_block_count": len(input.Content),
})
result, err := a.controller.Exec(ctx, agentruntime.ExecInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
TurnID: input.TurnID,
ClientSubmitID: input.ClientSubmitID,
CanonicalSubmitOccurredAtUnixMS: input.CanonicalSubmitOccurredAtUnixMS,
CapabilityRefs: runtimeCapabilityReferencesFromService(input.CapabilityRefs),
Content: runtimePromptContentFromService(input.Content),
DisplayPrompt: input.DisplayPrompt,
InitialTitle: input.InitialTitle,
InitialTitleBase: input.InitialTitleBase,
Guidance: input.Guidance,
HistoryReplacement: input.HistoryReplacement,
RequireProviderAcceptance: input.RequireProviderAcceptance,
Metadata: cloneRuntimeContext(input.Metadata),
TuttiModeSnapshot: runtimeTuttiModeSnapshotFromService(input.TuttiModeSnapshot),
})
projected := agentservice.RuntimeExecResult{
AgentSessionID: result.AgentSessionID,
Status: result.Status,
TurnID: result.TurnID,
Accepted: result.Accepted,
ProviderDispatch: serviceProviderDispatchFromRuntime(result.ProviderDispatch),
SessionStatus: result.SessionStatus,
TurnLifecycle: serviceTurnLifecycleFromRuntime(result.TurnLifecycle),
SubmitAvailability: serviceSubmitAvailabilityFromRuntime(result.SubmitAvailability),
}
if err != nil {
agentservice.LogSubmitTrace("runtime_adapter.exec.failed", input.WorkspaceID, input.AgentSessionID, input.ClientSubmitID, input.Metadata, map[string]any{
"error": err.Error(),
})
return projected, mapAgentRuntimeError(err)
}
agentservice.LogSubmitTrace("runtime_adapter.exec.resolved", input.WorkspaceID, input.AgentSessionID, input.ClientSubmitID, input.Metadata, map[string]any{
"turn_id": result.TurnID,
"session_status": result.SessionStatus,
"turn_phase": result.TurnLifecycle.Phase,
})
return projected, nil
}
func serviceProviderDispatchFromRuntime(
dispatch *agentruntime.ProviderDispatchResult,
) agenthost.RuntimeProviderDispatchResult {
if dispatch == nil {
return agenthost.RuntimeProviderDispatchResult{}
}
projected := agenthost.RuntimeProviderDispatchResult{
Disposition: agenthost.RuntimeDispatchDisposition(dispatch.Disposition),
}
if diagnostics := dispatch.AcceptanceDiagnostics; diagnostics != nil {
projected.AcceptanceDiagnostics = &agenthost.RuntimeProviderAcceptanceDiagnostics{
Status: diagnostics.Status,
ProviderSessionIDPresent: diagnostics.ProviderSessionIDPresent,
ProviderTurnIDPresent: diagnostics.ProviderTurnIDPresent,
ProviderTurnIDSource: diagnostics.ProviderTurnIDSource,
FailureReason: diagnostics.FailureReason,
}
}
if dispatch.Acceptance != nil {
projected.Acceptance = &agenthost.RuntimeProviderAcceptanceReceipt{
ProviderSessionID: dispatch.Acceptance.ProviderSessionID,
ProviderTurnID: dispatch.Acceptance.ProviderTurnID,
Source: agenthost.RuntimeAcceptanceSource(
dispatch.Acceptance.Source,
),
}
}
return projected
}
func (a agentRuntimeAdapter) DurablyReportSubmitProvenance(
ctx context.Context,
input agentservice.RuntimeSubmitProvenanceInput,
) error {
err := a.controller.DurablyReportSubmitProvenance(ctx, agentruntime.SubmitProvenanceInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
TurnID: input.TurnID,
ClientSubmitID: input.ClientSubmitID,
CanonicalSubmitOccurredAtUnixMS: input.CanonicalSubmitOccurredAtUnixMS,
Content: runtimePromptContentFromService(input.Content),
DisplayPrompt: input.DisplayPrompt,
Guidance: input.Guidance,
})
if err != nil {
return mapAgentRuntimeError(err)
}
return nil
}
func runtimeTuttiModeSnapshotFromService(
snapshot *agentservice.TuttiModeTurnSnapshot,
) *agentruntime.TuttiModeTurnSnapshot {
if snapshot == nil {
return nil
}
legacyOrchestrationIntensity := snapshot.OrchestrationIntensity //nolint:staticcheck // Compatibility bridge preserves version-zero snapshots.
return &agentruntime.TuttiModeTurnSnapshot{
ActivationID: snapshot.ActivationID,
RevisionID: snapshot.RevisionID,
Revision: snapshot.Revision,
State: snapshot.State,
Source: snapshot.Source,
PreferenceVersion: snapshot.PreferenceVersion,
Effect: snapshot.Effect,
Speed: snapshot.Speed,
OrchestrationIntensity: legacyOrchestrationIntensity,
}
}
func runtimeCapabilityReferencesFromService(
references []agentservice.CapabilityReference,
) []agentruntime.CapabilityReference {
if len(references) == 0 {
return nil
}
mapped := make([]agentruntime.CapabilityReference, 0, len(references))
for _, reference := range references {
mapped = append(mapped, agentruntime.CapabilityReference{
Capability: reference.Capability,
Source: reference.Source,
})
}
return mapped
}
func serviceSubmitAvailabilityFromRuntime(value agentruntime.SubmitAvailability) agentservice.SubmitAvailability {
return agentservice.SubmitAvailability{
State: value.State,
Reason: value.Reason,
}
}
func serviceCompletedCommandFromRuntime(value *agentruntime.CompletedCommand) *agentservice.CompletedCommand {
if value == nil {
return nil
}
return &agentservice.CompletedCommand{
Kind: value.Kind,
Status: value.Status,
}
}
func serviceTurnLifecycleFromRuntime(value agentruntime.TurnLifecycle) agentservice.TurnLifecycle {
return agentservice.TurnLifecycle{
ActiveTurnID: cloneStringPointer(value.ActiveTurnID),
Phase: value.Phase,
Settling: value.Settling,
Outcome: cloneStringPointer(value.Outcome),
CompletedCommand: serviceCompletedCommandFromRuntime(value.CompletedCommand),
}
}
func cloneStringPointer(value *string) *string {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
func (a agentRuntimeAdapter) ValidatePromptContent(ctx context.Context, input agentservice.RuntimeExecInput) error {
if err := a.controller.ValidatePromptContent(ctx, agentruntime.ExecInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
Content: runtimePromptContentFromService(input.Content),
DisplayPrompt: input.DisplayPrompt,
}); err != nil {
return mapAgentRuntimeError(err)
}
return nil
}
func runtimePromptContentFromService(content []agentservice.PromptContentBlock) []agentruntime.PromptContentBlock {
result := make([]agentruntime.PromptContentBlock, 0, len(content))
for _, block := range content {
result = append(result, agentruntime.PromptContentBlock{
Type: block.Type,
Text: block.Text,
MimeType: block.MimeType,
Data: block.Data,
URL: block.URL,
AttachmentID: block.AttachmentID,
Name: block.Name,
Path: block.Path,
ConnectorKey: block.ConnectorKey,
})
}
return result
}
func (a agentRuntimeAdapter) SubmitInteractive(ctx context.Context, input agentservice.RuntimeSubmitInteractiveInput) (agentservice.RuntimeSubmitInteractiveResult, error) {
result, err := a.controller.SubmitInteractive(ctx, agentruntime.SubmitInteractiveInput{
RoomID: input.WorkspaceID,
RootAgentSessionID: input.RootAgentSessionID,
AgentSessionID: input.AgentSessionID,
TurnID: input.TurnID,
RequestID: input.RequestID,
Action: input.Action,
OptionID: input.OptionID,
Payload: input.Payload,
})
mapped := agentservice.RuntimeSubmitInteractiveResult{
Disposition: agentservice.RuntimeInteractiveDisposition(result.Disposition),
FollowUpPrompt: result.FollowUpPrompt,
}
if err != nil {
return mapped, mapAgentRuntimeError(err)
}
return mapped, nil
}
func (a agentRuntimeAdapter) InteractiveDisposition(workspaceID string, rootAgentSessionID string, agentSessionID string, turnID string, requestID string) agentservice.RuntimeInteractiveDisposition {
return agentservice.RuntimeInteractiveDisposition(a.controller.InteractiveDisposition(workspaceID, rootAgentSessionID, agentSessionID, turnID, requestID))
}
func (a agentRuntimeAdapter) UpdateSettings(ctx context.Context, input agentservice.RuntimeUpdateSettingsInput) error {
if _, err := a.controller.UpdateSettings(ctx, agentruntime.UpdateSettingsInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
Settings: agentruntime.SessionSettingsPatch{
Model: input.Settings.Model,
ReasoningEffort: input.Settings.ReasoningEffort,
Speed: input.Settings.Speed,
PlanMode: input.Settings.PlanMode,
BrowserUse: input.Settings.BrowserUse,
PermissionModeID: input.Settings.PermissionModeID,
},
}); err != nil {
return mapAgentRuntimeError(err)
}
return nil
}
func (a agentRuntimeAdapter) Resume(ctx context.Context, input agentservice.RuntimeResumeInput) (agentservice.ProviderRuntimeSession, error) {
session, err := a.controller.Resume(ctx, agentruntime.ResumeInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
AgentTargetID: input.AgentTargetID,
Provider: input.Provider,
ProviderSessionID: input.ProviderSessionID,
Resumable: input.Resumable,
CWD: input.Cwd,
Env: append([]string(nil), input.Env...),
MCPServers: daemonMCPServerBindings(input.MCPServers),
Title: input.Title,
Status: input.Status,
Settings: agentRuntimeSessionSettings(input.Settings),
PermissionModeID: input.Settings.PermissionModeID,
CreatedAtUnixMS: input.CreatedAtUnixMS,
UpdatedAtUnixMS: input.UpdatedAtUnixMS,
Visible: input.Visible,
RuntimeContext: cloneRuntimeContext(input.RuntimeContext),
ProviderTargetRef: cloneRuntimeContext(input.ProviderTargetRef),
RecreateIfMissing: input.RecreateIfMissing,
})
if err != nil {
return agentservice.ProviderRuntimeSession{}, mapAgentRuntimeError(err)
}
return a.runtimeSessionWithState(session), nil
}
func (a agentRuntimeAdapter) Session(workspaceID string, agentSessionID string) (agentservice.ProviderRuntimeSession, bool) {
session, ok := a.controller.Session(workspaceID, agentSessionID)
if !ok {
return agentservice.ProviderRuntimeSession{}, false
}
return a.runtimeSessionWithState(session), true
}
func (a agentRuntimeAdapter) SetVisible(ctx context.Context, input agentservice.RuntimeSetVisibleInput) (agentservice.ProviderRuntimeSession, error) {
session, err := a.controller.SetVisible(ctx, input.WorkspaceID, input.AgentSessionID, input.Visible)
if err != nil {
return agentservice.ProviderRuntimeSession{}, mapAgentRuntimeError(err)
}
return a.runtimeSessionWithState(session), nil
}
func (a agentRuntimeAdapter) SetTitle(ctx context.Context, input agentservice.RuntimeSetTitleInput) (agentservice.ProviderRuntimeSession, error) {
session, err := a.controller.SetTitle(ctx, input.WorkspaceID, input.AgentSessionID, input.Title)
if err != nil {
return agentservice.ProviderRuntimeSession{}, mapAgentRuntimeError(err)
}
return a.runtimeSessionWithState(session), nil
}
func (a agentRuntimeAdapter) Sessions(workspaceID string) []agentservice.ProviderRuntimeSession {
sessions := a.controller.Sessions(workspaceID)
result := make([]agentservice.ProviderRuntimeSession, 0, len(sessions))
for _, session := range sessions {
result = append(result, a.runtimeSessionWithState(session))
}
return result
}
func (a agentRuntimeAdapter) Start(ctx context.Context, input agentservice.RuntimeStartInput) (agentservice.RuntimeStartResult, error) {
result, err := a.controller.Start(ctx, agentruntime.StartInput{
RoomID: input.WorkspaceID,
AgentSessionID: input.AgentSessionID,
AgentTargetID: input.AgentTargetID,
Provider: input.Provider,
CWD: input.Cwd,
Env: append([]string(nil), input.Env...),
MCPServers: daemonMCPServerBindings(input.MCPServers),
Title: input.Title,
InitialTitleEstablished: input.InitialTitleEstablished,
ProviderTargetRef: cloneRuntimeContext(input.ProviderTargetRef),
RuntimeContext: cloneRuntimeContext(input.RuntimeContext),
PermissionModeID: input.PermissionModeID,
Settings: &agentruntime.SessionSettings{
Model: input.Model,
ReasoningEffort: input.ReasoningEffort,
Speed: input.Speed,
PlanMode: input.PlanMode,
BrowserUse: cloneOptionalBool(input.BrowserUse),
PermissionModeID: input.PermissionModeID,
ConversationDetailMode: input.ConversationDetailMode,
},
Visible: input.Visible,
Provisional: input.Provisional,
CanonicalInitPending: input.CanonicalInitPending,
})
if err != nil {
return agentservice.RuntimeStartResult{}, mapAgentRuntimeError(err)
}
session := a.runtimeSessionWithState(result.Session)
session.Provisional = input.Provisional
return agentservice.RuntimeStartResult{Session: session, Created: result.Created}, nil
}
func (a agentRuntimeAdapter) PublishSessionInitialization(
ctx context.Context,
input agentservice.RuntimeSessionInitializationPublishInput,
) (agentservice.ProviderRuntimeSession, error) {
session, err := a.controller.PublishSessionInitialization(
ctx,
input.WorkspaceID,
input.AgentSessionID,
)
if err != nil {
return agentservice.ProviderRuntimeSession{}, mapAgentRuntimeError(err)
}
return a.runtimeSessionWithState(session), nil
}
func (a agentRuntimeAdapter) Subscribe(workspaceID string, agentSessionID string) (<-chan agentservice.RuntimeStreamEvent, func(), bool) {
events, unsubscribe, ok := a.controller.Subscribe(workspaceID, agentSessionID)
return agentRuntimeStreamEvents(events), unsubscribe, ok
}
func agentRuntimeStreamEvents(events <-chan agentruntime.StreamEvent) <-chan agentservice.RuntimeStreamEvent {
out := make(chan agentservice.RuntimeStreamEvent)
go func() {
defer close(out)
for event := range events {
out <- agentservice.RuntimeStreamEvent{
EventType: event.EventType,
Data: event.Data,
}
}
}()
return out
}
func agentRuntimeSession(session agentruntime.Session) agentservice.ProviderRuntimeSession {
return agentservice.ProviderRuntimeSession{
ID: session.AgentSessionID,
WorkspaceID: session.RoomID,
AgentTargetID: session.AgentTargetID,
Provider: session.Provider,
ProviderSessionID: session.ProviderSessionID,
Resumable: session.Resumable,
Cwd: session.CWD,
Env: append([]string(nil), session.Env...),
MCPServers: serviceMCPServerBindings(session.MCPServers),
Settings: agentRuntimeComposerSettings(session.Settings),
Status: session.Status,
TurnLifecycle: serviceTurnLifecyclePointerFromRuntime(session.TurnLifecycle),
SubmitAvailability: serviceSubmitAvailabilityPointerFromRuntime(session.SubmitAvailability),
Visible: session.Visible,
Title: session.Title,
InitialTitleEstablished: session.InitialTitleEstablished,
LastError: session.LastError,
RuntimeContext: cloneRuntimeContext(session.RuntimeContext),
CreatedAtUnixMS: session.CreatedAtUnixMS,
UpdatedAtUnixMS: session.UpdatedAtUnixMS,
}
}
func daemonMCPServerBindings(input []agenthost.MCPServerBinding) []agentruntime.MCPServerBinding {
if len(input) == 0 {
return nil
}
result := make([]agentruntime.MCPServerBinding, 0, len(input))
for _, binding := range input {
headers := make(map[string]string, len(binding.Headers))
for key, value := range binding.Headers {
headers[key] = value
}
result = append(result, agentruntime.MCPServerBinding{Name: binding.Name, Type: binding.Type, URL: binding.URL, Headers: headers})
}
return result
}
func serviceMCPServerBindings(input []agentruntime.MCPServerBinding) []agenthost.MCPServerBinding {
if len(input) == 0 {
return nil
}
result := make([]agenthost.MCPServerBinding, 0, len(input))
for _, binding := range input {
headers := make(map[string]string, len(binding.Headers))
for key, value := range binding.Headers {
headers[key] = value
}
result = append(result, agenthost.MCPServerBinding{Name: binding.Name, Type: binding.Type, URL: binding.URL, Headers: headers})
}
return result
}
func (a agentRuntimeAdapter) runtimeSessionWithState(session agentruntime.Session) agentservice.ProviderRuntimeSession {
result := agentRuntimeSession(session)
state, err := a.controller.State(session.RoomID, session.AgentSessionID)
if err != nil {
return result
}
if state.ProviderSessionID != "" {
result.ProviderSessionID = state.ProviderSessionID
}
result.Resumable = result.Resumable || state.Resumable
if state.Status != "" {
result.Status = state.Status
}
if state.TurnLifecycle != nil {
result.TurnLifecycle = serviceTurnLifecyclePointerFromRuntime(state.TurnLifecycle)
}
if state.SubmitAvailability != nil {
result.SubmitAvailability = serviceSubmitAvailabilityPointerFromRuntime(state.SubmitAvailability)
}
if state.Settings != nil {
result.Settings = agentRuntimeComposerSettings(state.Settings)
}
result.Capabilities = canonical.CloneCapabilitySnapshot(state.Capabilities)
result.RuntimeContext = cloneRuntimeContext(state.RuntimeContext)
if state.UpdatedAtUnixMS > 0 {
result.UpdatedAtUnixMS = state.UpdatedAtUnixMS
}
return result
}
func serviceSubmitAvailabilityPointerFromRuntime(value *agentruntime.SubmitAvailability) *agentservice.SubmitAvailability {
if value == nil {
return nil
}
converted := serviceSubmitAvailabilityFromRuntime(*value)
return &converted
}
func serviceTurnLifecyclePointerFromRuntime(value *agentruntime.TurnLifecycle) *agentservice.TurnLifecycle {
if value == nil {
return nil
}
converted := serviceTurnLifecycleFromRuntime(*value)
return &converted
}
func agentRuntimeComposerSettings(settings *agentruntime.SessionSettings) *agentservice.ComposerSettings {
if settings == nil {
return nil
}
return &agentservice.ComposerSettings{
Model: settings.Model,
PermissionModeID: settings.PermissionModeID,
PlanMode: settings.PlanMode,
BrowserUse: cloneOptionalBool(settings.BrowserUse),
ReasoningEffort: settings.ReasoningEffort,
Speed: settings.Speed,
ConversationDetailMode: settings.ConversationDetailMode,
}
}
func cloneRuntimeContext(value map[string]any) map[string]any {
if len(value) == 0 {
return nil
}
cloned := make(map[string]any, len(value))
for key, item := range value {
cloned[key] = cloneRuntimeContextValue(item)
}
return cloned
}
func cloneRuntimeContextValue(value any) any {
switch typed := value.(type) {
case map[string]any:
out := make(map[string]any, len(typed))
for key, item := range typed {
out[key] = cloneRuntimeContextValue(item)
}
return out
case []any:
out := make([]any, len(typed))
for index, item := range typed {
out[index] = cloneRuntimeContextValue(item)
}
return out
default:
return value
}
}
func cloneOptionalBool(value *bool) *bool {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
func mapAgentRuntimeError(err error) error {
if err == nil {
return nil
}
if errors.Is(err, agentruntime.ErrSessionNotFound) {
return agentservice.ErrSessionNotFound
}
if errors.Is(err, agentruntime.ErrSessionDisconnected) {
return fmt.Errorf("%w: %v", agentservice.ErrRuntimeSessionDisconnected, err)
}
if errors.Is(err, agentruntime.ErrInteractiveRequestNotLive) {
return fmt.Errorf("%w: %v", agentservice.ErrInteractiveRequestNotLive, err)
}
if errors.Is(err, agentruntime.ErrInteractiveAlreadyAnswered) {
return fmt.Errorf("%w: %v", agentservice.ErrInteractiveAlreadyAnswered, err)
}
if errors.Is(err, agentruntime.ErrSessionNoActiveTurn) {
return agentservice.ErrSessionNoActiveTurn
}
if errors.Is(err, agentruntime.ErrActiveTurnGuidanceUnsupported) {
return agentservice.ErrActiveTurnGuidanceUnsupported
}
if errors.Is(err, agentruntime.ErrActiveTurnTargetRequired) {
return agentservice.ErrActiveTurnTargetRequired
}
if errors.Is(err, agentruntime.ErrActiveTurnTargetMismatch) {
return agentservice.ErrActiveTurnTargetMismatch
}
if errors.Is(err, agentruntime.ErrSessionSettingsRequireNewSession) {
return agentservice.ErrSessionSettingsRequireNewSession
}
if errors.Is(err, agentruntime.ErrPromptImageUnsupported) {
return agentservice.ErrPromptImageUnsupported
}
var appErr *agentruntime.AppError
if errors.As(err, &appErr) && appErr != nil {
return agenthost.NewProviderError(appErr.Code, appErr.Message, appErr.DebugMessage, appErr)
}
return err
}