Documentation
¶
Overview ¶
Package server is the API adapter for mecatl: it exposes the WP8 agent loop over two network surfaces that share one domain Event taxonomy.
- gRPC (primary): HarnessServer implements the generated mecatlv1.HarnessServiceServer. The bidi Converse stream carries a whole run: a mandatory first Prompt frame, then zero or more ResumeApproval / Cancel control frames, while the server streams Event envelopes until the terminal result.
- HTTP/SSE (pragmatic): HTTPHandler serves POST /v1/sessions, GET /v1/sessions/{id}, POST /v1/sessions/{id}/prompt (text/event-stream), POST /v1/sessions/{id}/approve and POST /v1/sessions/{id}/cancel over the same *agent.Engine and the same session.Event.
Both surfaces translate session.Event into the proto Event with the pure toProto mapper; neither surface ever sees an OpenAI type. As an adapter this package MAY import engine/agent, engine/session, engine/tool, engine/port and the generated contracts/gen/go.
Required-field validation is enforced here in Go (the proto carries buf.validate annotations for documentation and future runtime enforcement; wiring the protovalidate runtime is a deferred follow-up).
Index ¶
- Constants
- Variables
- func ClampInt32(v int) int32
- func ClassifyAllBoundaries() []error
- func ClassifyCallerStoreBoundaries() []error
- func ClassifyServiceBoundaries() []error
- func ClassifySystemBoundaries() []error
- func DeriveTitle(sess *session.Session) string
- func NewScheduleManager(cfg ScheduleManagerConfig) *scheduleManager
- func PlanManualRetention(rows []port.SessionDiscoveryMeta, policy RetentionPolicy, ...) sessionretention.Result
- func PresentScheduleName(sched port.Schedule) string
- func ValidateClassifiedNames(surface string, table map[string]ClassificationEntry, boundaries []string) []error
- type ACPEnvironmentOverlay
- type AccessKind
- type ActivityGapError
- type ActivityReplayStatus
- type AuthenticationRejectionCategorizer
- type Authenticator
- type CORSPolicy
- type CapabilityReason
- type ClassificationEntry
- type CleanupCandidate
- type CleanupCounts
- type CleanupItemError
- type CleanupJob
- type CleanupPlan
- type CleanupScope
- type ClientMCPGrant
- type Clock
- type Command
- type CommandLister
- type Config
- type CreateSessionOption
- func WithClientMCP(grant ClientMCPGrant) CreateSessionOption
- func WithDebugMCP(names []string) CreateSessionOption
- func WithDebugTarget(id session.SessionID) CreateSessionOption
- func WithOwner(p *session.Principal) CreateSessionOption
- func WithPlacementBinding(binding PlacementBinding) CreateSessionOption
- func WithScheduledRelationship(scheduleName string, origin session.SessionID) CreateSessionOption
- func WithSessionID(id session.SessionID) CreateSessionOption
- func WithSourceSession(id session.SessionID) CreateSessionOption
- type DebugSessionEngineFactory
- type DreamCapabilities
- type DreamDecision
- type DreamOperation
- type DreamParticipant
- type DreamReceipt
- type DreamReplacement
- type DreamReview
- type DreamReviewer
- type DreamTarget
- type DreamTargetCapability
- type ExplicitReflector
- type FeatureScope
- type ForkSuccessorRequest
- type HTTPHandler
- type HarnessServer
- func (h *HarnessServer) AbandonLearningAttempt(ctx context.Context, req *mecatlv1.MutateLearningAttemptRequest) (*mecatlv1.MutateLearningAttemptResponse, error)
- func (h *HarnessServer) ActivateLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (h *HarnessServer) ApplySessionCleanup(ctx context.Context, req *mecatlv1.ApplySessionCleanupRequest) (*mecatlv1.CleanupJob, error)
- func (h *HarnessServer) ApplySessionMigration(ctx context.Context, req *mecatlv1.ApplySessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
- func (h *HarnessServer) ApprovePlan(req *mecatlv1.ApprovePlanRequest, ...) error
- func (h *HarnessServer) ArchiveLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (h *HarnessServer) CancelSessionCleanup(ctx context.Context, req *mecatlv1.CancelSessionCleanupRequest) (*mecatlv1.CleanupJob, error)
- func (h *HarnessServer) CancelSessionMigration(ctx context.Context, req *mecatlv1.CancelSessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
- func (h *HarnessServer) CancelTeammate(ctx context.Context, req *mecatlv1.CancelTeammateRequest) (*mecatlv1.CancelTeammateResponse, error)
- func (h *HarnessServer) CleanupTeam(ctx context.Context, req *mecatlv1.CleanupTeamRequest) (*mecatlv1.CleanupTeamResponse, error)
- func (h *HarnessServer) ClearSession(ctx context.Context, req *mecatlv1.ClearSessionRequest) (*mecatlv1.ClearSessionResponse, error)
- func (h *HarnessServer) CloseSession(ctx context.Context, req *mecatlv1.CloseSessionRequest) (*mecatlv1.CloseSessionResponse, error)
- func (h *HarnessServer) CompactSession(ctx context.Context, req *mecatlv1.CompactSessionRequest) (*mecatlv1.CompactSessionResponse, error)
- func (h *HarnessServer) Converse(stream mecatlv1.HarnessService_ConverseServer) error
- func (h *HarnessServer) CreateSession(ctx context.Context, req *mecatlv1.CreateSessionRequest) (*mecatlv1.CreateSessionResponse, error)
- func (h *HarnessServer) CreateTeam(ctx context.Context, req *mecatlv1.CreateTeamRequest) (*mecatlv1.CreateTeamResponse, error)
- func (h *HarnessServer) DecideDreamPlan(ctx context.Context, req *mecatlv1.DecideDreamPlanRequest) (*mecatlv1.DecideDreamPlanResponse, error)
- func (h *HarnessServer) DecideLearningProposal(ctx context.Context, req *mecatlv1.DecideLearningProposalRequest) (*mecatlv1.DecideLearningProposalResponse, error)
- func (h *HarnessServer) DeleteSession(ctx context.Context, req *mecatlv1.DeleteSessionRequest) (*mecatlv1.DeleteSessionResponse, error)
- func (h *HarnessServer) DiffLearnedSkillVersions(ctx context.Context, req *mecatlv1.DiffLearnedSkillVersionsRequest) (*mecatlv1.DiffLearnedSkillVersionsResponse, error)
- func (h *HarnessServer) ForkSession(ctx context.Context, req *mecatlv1.ForkSessionRequest) (*mecatlv1.ForkSessionResponse, error)
- func (h *HarnessServer) GenerateDreamPlan(ctx context.Context, req *mecatlv1.GenerateDreamPlanRequest) (*mecatlv1.GenerateDreamPlanResponse, error)
- func (h *HarnessServer) GetCompatibilityInfo(ctx context.Context, _ *mecatlv1.GetCompatibilityInfoRequest) (*mecatlv1.GetCompatibilityInfoResponse, error)
- func (h *HarnessServer) GetLearnedSkill(ctx context.Context, req *mecatlv1.GetLearnedSkillRequest) (*mecatlv1.GetLearnedSkillResponse, error)
- func (h *HarnessServer) GetLearningAttempt(ctx context.Context, req *mecatlv1.GetLearningAttemptRequest) (*mecatlv1.GetLearningAttemptResponse, error)
- func (h *HarnessServer) GetLearningProposal(ctx context.Context, req *mecatlv1.GetLearningProposalRequest) (*mecatlv1.GetLearningProposalResponse, error)
- func (h *HarnessServer) GetMcpPrompt(ctx context.Context, req *mecatlv1.GetMcpPromptRequest) (*mecatlv1.GetMcpPromptResponse, error)
- func (h *HarnessServer) GetServerInfo(_ context.Context, req *mecatlv1.GetServerInfoRequest) (*mecatlv1.GetServerInfoResponse, error)
- func (h *HarnessServer) GetSession(ctx context.Context, req *mecatlv1.GetSessionRequest) (*mecatlv1.GetSessionResponse, error)
- func (h *HarnessServer) GetSessionCleanupJob(ctx context.Context, req *mecatlv1.GetSessionCleanupJobRequest) (*mecatlv1.CleanupJob, error)
- func (h *HarnessServer) GetSessionMigrationJob(ctx context.Context, req *mecatlv1.GetSessionMigrationJobRequest) (*mecatlv1.SessionMigrationJob, error)
- func (h *HarnessServer) GetSessionTranscript(ctx context.Context, req *mecatlv1.GetSessionTranscriptRequest) (*mecatlv1.GetSessionTranscriptResponse, error)
- func (h *HarnessServer) GetSoul(ctx context.Context, _ *mecatlv1.GetSoulRequest) (*mecatlv1.GetSoulResponse, error)
- func (h *HarnessServer) GetStorageHealth(ctx context.Context, _ *mecatlv1.GetStorageHealthRequest) (*mecatlv1.GetStorageHealthResponse, error)
- func (h *HarnessServer) GetUserModel(ctx context.Context, req *mecatlv1.GetUserModelRequest) (*mecatlv1.GetUserModelResponse, error)
- func (h *HarnessServer) ListAgents(ctx context.Context, _ *mecatlv1.ListAgentsRequest) (*mecatlv1.ListAgentsResponse, error)
- func (h *HarnessServer) ListCommands(ctx context.Context, req *mecatlv1.ListCommandsRequest) (*mecatlv1.ListCommandsResponse, error)
- func (h *HarnessServer) ListLearnedSkills(ctx context.Context, req *mecatlv1.ListLearnedSkillsRequest) (*mecatlv1.ListLearnedSkillsResponse, error)
- func (h *HarnessServer) ListLearningAttempts(ctx context.Context, req *mecatlv1.ListLearningAttemptsRequest) (*mecatlv1.ListLearningAttemptsResponse, error)
- func (h *HarnessServer) ListLearningProposals(ctx context.Context, req *mecatlv1.ListLearningProposalsRequest) (*mecatlv1.ListLearningProposalsResponse, error)
- func (h *HarnessServer) ListMcpPrompts(ctx context.Context, req *mecatlv1.ListMcpPromptsRequest) (*mecatlv1.ListMcpPromptsResponse, error)
- func (h *HarnessServer) ListMcpResources(ctx context.Context, req *mecatlv1.ListMcpResourcesRequest) (*mecatlv1.ListMcpResourcesResponse, error)
- func (h *HarnessServer) ListMcpSources(ctx context.Context, _ *mecatlv1.ListMcpSourcesRequest) (*mecatlv1.ListMcpSourcesResponse, error)
- func (h *HarnessServer) ListModels(ctx context.Context, _ *mecatlv1.ListModelsRequest) (*mecatlv1.ListModelsResponse, error)
- func (h *HarnessServer) ListSessions(ctx context.Context, req *mecatlv1.ListSessionsRequest) (*mecatlv1.ListSessionsResponse, error)
- func (h *HarnessServer) ListSkillChanges(ctx context.Context, req *mecatlv1.ListSkillChangesRequest) (*mecatlv1.ListSkillChangesResponse, error)
- func (h *HarnessServer) ListSkills(ctx context.Context, _ *mecatlv1.ListSkillsRequest) (*mecatlv1.ListSkillsResponse, error)
- func (h *HarnessServer) ListTeam(ctx context.Context, req *mecatlv1.ListTeamRequest) (*mecatlv1.ListTeamResponse, error)
- func (h *HarnessServer) ListToolHiveGroups(ctx context.Context, _ *mecatlv1.ListToolHiveGroupsRequest) (*mecatlv1.ListToolHiveGroupsResponse, error)
- func (h *HarnessServer) ListWorktrees(ctx context.Context, req *mecatlv1.ListWorktreesRequest) (*mecatlv1.ListWorktreesResponse, error)
- func (h *HarnessServer) PlanSessionCleanup(ctx context.Context, req *mecatlv1.PlanSessionCleanupRequest) (*mecatlv1.PlanSessionCleanupResponse, error)
- func (h *HarnessServer) PlanSessionMigration(ctx context.Context, _ *mecatlv1.PlanSessionMigrationRequest) (*mecatlv1.SessionMigrationPlan, error)
- func (h *HarnessServer) ReadMcpResource(ctx context.Context, req *mecatlv1.ReadMcpResourceRequest) (*mecatlv1.ReadMcpResourceResponse, error)
- func (h *HarnessServer) ReflectSession(ctx context.Context, req *mecatlv1.ReflectSessionRequest) (*mecatlv1.ReflectSessionResponse, error)
- func (h *HarnessServer) RejectLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (h *HarnessServer) RenameSession(ctx context.Context, req *mecatlv1.RenameSessionRequest) (*mecatlv1.RenameSessionResponse, error)
- func (h *HarnessServer) ResumeSessionMigration(ctx context.Context, req *mecatlv1.ResumeSessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
- func (h *HarnessServer) RetryLearningAttempt(ctx context.Context, req *mecatlv1.MutateLearningAttemptRequest) (*mecatlv1.MutateLearningAttemptResponse, error)
- func (h *HarnessServer) RollbackLearnedSkill(ctx context.Context, req *mecatlv1.RollbackLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (h *HarnessServer) RunTeam(req *mecatlv1.RunTeamRequest, stream mecatlv1.HarnessService_RunTeamServer) error
- func (h *HarnessServer) SendTeammateMessage(ctx context.Context, req *mecatlv1.SendTeammateMessageRequest) (*mecatlv1.SendTeammateMessageResponse, error)
- func (h *HarnessServer) SetMode(ctx context.Context, req *mecatlv1.SetModeRequest) (*mecatlv1.SetModeResponse, error)
- func (h *HarnessServer) SpawnTeammate(ctx context.Context, req *mecatlv1.SpawnTeammateRequest) (*mecatlv1.SpawnTeammateResponse, error)
- func (h *HarnessServer) StreamSessionEvents(req *mecatlv1.StreamSessionEventsRequest, ...) error
- func (h *HarnessServer) StreamSessionLive(req *mecatlv1.StreamSessionLiveRequest, ...) error
- func (h *HarnessServer) UndoLearningPromotion(ctx context.Context, req *mecatlv1.UndoLearningPromotionRequest) (*mecatlv1.UndoLearningPromotionResponse, error)
- func (h *HarnessServer) WatchSessionEvents(req *mecatlv1.WatchSessionEventsRequest, ...) error
- type HealthHandler
- type IDGenerator
- type ListSessionsPage
- type ListSessionsPageRequest
- type MemberEngineFactory
- type MigrationItemError
- type MigrationJob
- type MigrationPlan
- type OwnerlessCutoverInventory
- type PlacementBindRequest
- type PlacementBinder
- type PlacementBinding
- type PlacementDiscoverer
- type PlacementDiscoveryRequest
- type PlacementMetadata
- type PlacementOperation
- type PlacementProvider
- type PlacementReattachRequest
- type PlacementReattacher
- type PlacementScope
- type PlacementSelector
- type PlacementSelectorKind
- type PrincipalValidator
- type ProposalPromoter
- type ProposalUndoer
- type ProviderSelector
- type ReadyFunc
- type ReflectionReceipt
- type ResolvedModel
- type RetentionPolicy
- type RunEventRecorder
- type ScheduleManagerConfig
- type ScheduleManagerImpl
- type ScheduleServer
- func (h *ScheduleServer) CreateSchedule(ctx context.Context, req *mecatlv1.CreateScheduleRequest) (*mecatlv1.CreateScheduleResponse, error)
- func (h *ScheduleServer) DeleteSchedule(ctx context.Context, req *mecatlv1.DeleteScheduleRequest) (*mecatlv1.DeleteScheduleResponse, error)
- func (h *ScheduleServer) FireNow(ctx context.Context, req *mecatlv1.FireNowRequest) (*mecatlv1.FireNowResponse, error)
- func (h *ScheduleServer) GetFire(ctx context.Context, req *mecatlv1.GetFireRequest) (*mecatlv1.GetFireResponse, error)
- func (h *ScheduleServer) GetSchedule(ctx context.Context, req *mecatlv1.GetScheduleRequest) (*mecatlv1.GetScheduleResponse, error)
- func (h *ScheduleServer) ListFires(ctx context.Context, req *mecatlv1.ListFiresRequest) (*mecatlv1.ListFiresResponse, error)
- func (h *ScheduleServer) ListSchedules(ctx context.Context, _ *mecatlv1.ListSchedulesRequest) (*mecatlv1.ListSchedulesResponse, error)
- func (h *ScheduleServer) PauseSchedule(ctx context.Context, req *mecatlv1.PauseScheduleRequest) (*mecatlv1.PauseScheduleResponse, error)
- func (h *ScheduleServer) ResumeSchedule(ctx context.Context, req *mecatlv1.ResumeScheduleRequest) (*mecatlv1.ResumeScheduleResponse, error)
- func (h *ScheduleServer) UpdateSchedule(ctx context.Context, req *mecatlv1.UpdateScheduleRequest) (*mecatlv1.UpdateScheduleResponse, error)
- type ScopedWorktree
- type SecurityConfig
- type Service
- func (s *Service) AbandonLearningAttempt(ctx context.Context, id, expectedVersion string) (*mecatlv1.LearningAttempt, error)
- func (s *Service) ActivateLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (s *Service) ActiveRuns() int
- func (s *Service) ApplySessionCleanup(ctx context.Context, token string) (CleanupJob, error)
- func (s *Service) ApplySessionMigration(ctx context.Context, planID string, batchSize int) (MigrationJob, error)
- func (s *Service) Approve(ctx context.Context, id session.SessionID, askID string, ...) error
- func (s *Service) ApprovePlan(ctx context.Context, id session.SessionID, targetMode session.PermissionMode, ...) (<-chan session.Event, error)
- func (s *Service) ApproveRun(ctx context.Context, id session.SessionID, askID string, ...) (*agent.Run, error)
- func (s *Service) ArchiveLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (s *Service) BindPlacement(ctx context.Context, selector PlacementSelector, operation PlacementOperation) (PlacementBinding, error)
- func (s *Service) CanProcessSchedule(sched port.Schedule) bool
- func (s *Service) Cancel(ctx context.Context, id session.SessionID, expectedRunID string) error
- func (s *Service) CancelChild(ctx context.Context, id session.SessionID, childID string) error
- func (s *Service) CancelSessionCleanup(ctx context.Context, id string) (CleanupJob, error)
- func (s *Service) CancelSessionMigration(ctx context.Context, id string) (MigrationJob, error)
- func (s *Service) CancelSteer(ctx context.Context, id session.SessionID) (agent.SteerOutcome, error)
- func (s *Service) CancelTeammate(ctx context.Context, teamID, member string) error
- func (s *Service) CleanupTeam(ctx context.Context, teamID string) error
- func (s *Service) ClearSessionSuccessor(ctx context.Context, source session.SessionID, placement SuccessorPlacement) (session.SessionID, error)
- func (s *Service) ClientMCPFromWire(servers []mcp.ClientServer) (ClientMCPGrant, error)
- func (s *Service) Close()
- func (s *Service) CloseSession(id session.SessionID)
- func (s *Service) CompactSession(ctx context.Context, id session.SessionID, caller *session.Principal) (agent.ManualCompactionResult, error)
- func (s *Service) CompatibilityInfo(context.Context) *mecatlv1.GetCompatibilityInfoResponse
- func (s *Service) CreateACPSession(ctx context.Context, cwd string, mode session.PermissionMode, ...) (*session.Session, error)
- func (s *Service) CreateSchedule(ctx context.Context, spec port.ScheduleSpec) (port.Schedule, error)
- func (s *Service) CreateSession(ctx context.Context, mode session.PermissionMode, limits session.Limits) (*session.Session, error)
- func (s *Service) CreateSessionWithMCP(ctx context.Context, mode session.PermissionMode, limits session.Limits, ...) (*session.Session, error)
- func (s *Service) CreateSessionWithProfile(ctx context.Context, mode session.PermissionMode, limits session.Limits, ...) (*session.Session, error)
- func (s *Service) CreateSessionWithProvider(ctx context.Context, mode session.PermissionMode, limits session.Limits, ...) (*session.Session, error)
- func (s *Service) CreateTeamForSession(ctx context.Context, source session.SessionID, name, goal string, ...) (string, []team.Member, error)
- func (s *Service) CreateTeamOnDefaultPlacement(ctx context.Context, name, goal string, maxTeamTokens int, ...) (string, []team.Member, error)
- func (s *Service) DecideDream(ctx context.Context, id string, decision DreamDecision) (DreamReceipt, error)
- func (s *Service) DecideLearningProposal(ctx context.Context, id, expected, decision, reason, project string) (*mecatlv1.LearningProposal, error)
- func (s *Service) DeleteSchedule(ctx context.Context, name string) error
- func (s *Service) DeleteSession(ctx context.Context, id session.SessionID) error
- func (s *Service) DeleteSessionForRetention(ctx context.Context, id session.SessionID) error
- func (s *Service) DeleteSessionForRetentionCandidate(ctx context.Context, candidate port.SessionDiscoveryMeta) error
- func (s *Service) Diagnostics() port.Diagnostics
- func (s *Service) DiffLearnedSkillVersions(ctx context.Context, request *mecatlv1.DiffLearnedSkillVersionsRequest) (*mecatlv1.DiffLearnedSkillVersionsResponse, error)
- func (s *Service) Drain()
- func (s *Service) EmitScheduleEvent(ctx context.Context, payload session.SchedulePayload)
- func (s *Service) EndSession(ctx context.Context, id session.SessionID) error
- func (s *Service) FinishRun(id session.SessionID, run *agent.Run)
- func (s *Service) FireNow(ctx context.Context, name string) (port.ScheduleFire, error)
- func (s *Service) ForkSessionSuccessor(ctx context.Context, req ForkSuccessorRequest) (session.SessionID, error)
- func (s *Service) GenerateDream(ctx context.Context, target DreamTarget) (DreamReview, error)
- func (s *Service) GetFire(ctx context.Context, fireID string) (port.ScheduleFire, error)
- func (s *Service) GetLearnedSkill(ctx context.Context, request *mecatlv1.GetLearnedSkillRequest) (*mecatlv1.GetLearnedSkillResponse, error)
- func (s *Service) GetLearningAttempt(ctx context.Context, id string) (*mecatlv1.LearningAttempt, error)
- func (s *Service) GetLearningProposal(ctx context.Context, id, project string) (*mecatlv1.LearningProposal, error)
- func (s *Service) GetMcpPrompt(ctx context.Context, server, name string, args map[string]string) (mcp.PromptResult, error)
- func (s *Service) GetSchedule(ctx context.Context, name string) (port.Schedule, error)
- func (s *Service) GetSession(ctx context.Context, id session.SessionID) (*session.Session, error)
- func (s *Service) GetSoul(_ context.Context) *mecatlv1.SoulInfo
- func (s *Service) GetTranscript(ctx context.Context, id session.SessionID) (*SessionTranscript, error)
- func (s *Service) GetUserModel(ctx context.Context) (*mecatlv1.GetUserModelResponse, error)
- func (s *Service) GetUserModelDetail(ctx context.Context, key string) (*mecatlv1.UserModelDetail, error)
- func (s *Service) GracefulDrain(ctx context.Context) error
- func (s *Service) HasScheduler() bool
- func (s *Service) IsDraining() bool
- func (s *Service) IsLive(id session.SessionID) bool
- func (s *Service) LeaseSweepDisabled() bool
- func (s *Service) ListAgents(_ context.Context) []*mecatlv1.AgentInfo
- func (s *Service) ListCommandsForSession(ctx context.Context, id session.SessionID) ([]Command, error)
- func (s *Service) ListFires(ctx context.Context, scheduleName string) ([]port.ScheduleFire, error)
- func (s *Service) ListLearnedSkills(ctx context.Context, request *mecatlv1.ListLearnedSkillsRequest) (*mecatlv1.ListLearnedSkillsResponse, error)
- func (s *Service) ListLearningAttempts(ctx context.Context, stateValue, cursor string, limit int) (*mecatlv1.ListLearningAttemptsResponse, error)
- func (s *Service) ListLearningProposals(ctx context.Context, statusValue, cursor string, limit int, project string) (*mecatlv1.ListLearningProposalsResponse, error)
- func (s *Service) ListMcpPrompts(ctx context.Context, server string) ([]mcp.Prompt, error)
- func (s *Service) ListMcpResources(ctx context.Context, server string) ([]mcp.Resource, error)
- func (s *Service) ListMcpSources(ctx context.Context) []source.SourceInfo
- func (s *Service) ListModels(ctx context.Context) []*mecatlv1.ModelInfo
- func (s *Service) ListSchedules(ctx context.Context) ([]port.Schedule, error)
- func (s *Service) ListSessionPage(ctx context.Context, request ListSessionsPageRequest) (ListSessionsPage, error)
- func (s *Service) ListSessions(ctx context.Context) ([]SessionSummary, error)
- func (s *Service) ListSkillChanges(ctx context.Context, r *mecatlv1.ListSkillChangesRequest) (*mecatlv1.ListSkillChangesResponse, error)
- func (s *Service) ListSkills(ctx context.Context) []*mecatlv1.SkillInfo
- func (s *Service) ListTeam(ctx context.Context, teamID string) ([]team.Member, []team.Task, bool, error)
- func (s *Service) ListToolHiveGroups(ctx context.Context) []string
- func (s *Service) ListWorktreesForSession(ctx context.Context, id session.SessionID) ([]ScopedWorktree, error)
- func (s *Service) LoadACPSession(ctx context.Context, id session.SessionID, cwd string, ...) (*session.Session, error)
- func (s *Service) LoadSession(ctx context.Context, id session.SessionID) (*session.Session, error)
- func (s *Service) LoadSessionWithMCP(ctx context.Context, id session.SessionID, specs []mcp.ServerConfig) (*session.Session, error)
- func (s *Service) LookupRun(id session.SessionID) (*agent.Run, bool)
- func (s *Service) LookupSteerMessageID(id session.SessionID) string
- func (s *Service) MaintenanceMutationAvailable() bool
- func (s *Service) ManualDreamCapabilities() DreamCapabilities
- func (s *Service) MaybeAutoApprovePlan(ctx context.Context, id session.SessionID, ev session.Event)
- func (s *Service) OwnershipEnforced() bool
- func (s *Service) PauseSchedule(ctx context.Context, name string) error
- func (s *Service) Persist(ctx context.Context, id session.SessionID)
- func (s *Service) PlanSessionCleanup(ctx context.Context, scope CleanupScope) (CleanupPlan, error)
- func (s *Service) PlanSessionMigration(ctx context.Context) (MigrationPlan, error)
- func (s *Service) ProviderCapabilities() port.ProviderCapabilities
- func (s *Service) ProviderStatuses() []*mecatlv1.ProviderStatus
- func (s *Service) PublishSessionEvent(id session.SessionID, ev session.Event)
- func (s *Service) ReadMcpResource(ctx context.Context, server, uri string) (mcp.ResourceContents, error)
- func (s *Service) ReattachPlacement(ctx context.Context, ref session.EnvironmentRef) (PlacementBinding, error)
- func (s *Service) ReattachPlacementInScope(ctx context.Context, ref session.EnvironmentRef, scope string) (PlacementBinding, error)
- func (s *Service) RecoverNotice(id session.SessionID) string
- func (s *Service) ReflectSession(ctx context.Context, id session.SessionID) (*mecatlv1.ReflectionReceipt, error)
- func (s *Service) RejectLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (s *Service) RenameSession(ctx context.Context, id session.SessionID, title string) (*session.Session, error)
- func (s *Service) ResolvedModel(id session.SessionID) ResolvedModel
- func (s *Service) ResumeSchedule(ctx context.Context, name string) error
- func (s *Service) ResumeSessionMigration(ctx context.Context, jobID string, batchSize int) (MigrationJob, error)
- func (s *Service) RetryFailedRun(ctx context.Context, id session.SessionID) (*agent.Run, error)
- func (s *Service) RetryLearningAttempt(ctx context.Context, id, expectedVersion string) (*mecatlv1.LearningAttempt, error)
- func (s *Service) RollbackLearnedSkill(ctx context.Context, r *mecatlv1.RollbackLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
- func (s *Service) RunTeam(ctx context.Context, teamID string, sink func(agent.TeamEvent)) (agent.TeamOutcome, error)
- func (s *Service) ScheduleManager() port.ScheduleManager
- func (s *Service) SendTeammateMessage(ctx context.Context, teamID, from, to, body string) error
- func (s *Service) SessionCapabilities(id session.SessionID) port.ProviderCapabilities
- func (s *Service) SessionCleanupJob(ctx context.Context, id string) (CleanupJob, error)
- func (s *Service) SessionMigrationJob(ctx context.Context, id string) (MigrationJob, error)
- func (s *Service) SessionStale(ctx context.Context, meta port.SessionMeta) bool
- func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode session.PermissionMode) (*session.Session, error)
- func (s *Service) SetModels(models []*mecatlv1.ModelInfo)
- func (s *Service) SetModelsRefresher(fn func(context.Context))
- func (s *Service) SetProviderStatus(status []*mecatlv1.ProviderStatus)
- func (s *Service) SetScheduleMinInterval(d time.Duration)
- func (s *Service) SetScheduler(sch *scheduler.Scheduler)
- func (s *Service) SetSessionEnvironment(id session.SessionID, env tool.Environment)
- func (s *Service) SettleIfStale(ctx context.Context, id session.SessionID) (bool, error)
- func (s *Service) SpawnTeammate(ctx context.Context, teamID string, spec agent.MemberSpec) (team.Member, error)
- func (s *Service) StaleRunningCandidates(ctx context.Context) ([]port.SessionMeta, error)
- func (s *Service) StartRun(ctx context.Context, id session.SessionID, text string) (*agent.Run, error)
- func (s *Service) StartRunContent(ctx context.Context, id session.SessionID, text string, ...) (*agent.Run, error)
- func (s *Service) StartScheduledRunContent(ctx context.Context, id session.SessionID, text string, ...) (*agent.Run, error)
- func (s *Service) Steer(ctx context.Context, id session.SessionID, text string, ...) (agent.SteerOutcome, bool, *agent.Run, error)
- func (s *Service) StorageHealth(ctx context.Context) (StorageHealth, error)
- func (s *Service) StorageReady(ctx context.Context) bool
- func (s *Service) StreamSessionEvents(ctx context.Context, id session.SessionID) (iter.Seq2[session.Event, error], error)
- func (s *Service) Subscribe(ctx context.Context, id session.SessionID) (<-chan session.Event, func(), error)
- func (s *Service) UndoLearningPromotion(ctx context.Context, id, expected, project string) (*mecatlv1.LearningProposal, error)
- func (s *Service) UpdateSchedule(ctx context.Context, spec port.ScheduleSpec) (port.Schedule, error)
- func (s *Service) WatchSessionEvents(ctx context.Context, id session.SessionID, after port.Cursor, runID string) (iter.Seq2[WatchEnvelope, error], error)
- func (s *Service) WithAuthorizedSession(ctx context.Context, id session.SessionID, effect func(*session.Session) error) (*session.Session, error)
- type SessionEngineFactory
- type SessionEngineResult
- type SessionInventoryActionReasons
- type SessionInventoryCapabilities
- type SessionMutationCapability
- func (c *SessionMutationCapability) Disable()
- func (c *SessionMutationCapability) Grant(id session.SessionID)
- func (c *SessionMutationCapability) GuardStore(next port.SessionStore) port.SessionStore
- func (c *SessionMutationCapability) GuardToolCallRecorder(next port.ToolCallRecorder) port.ToolCallRecorder
- func (c *SessionMutationCapability) Invalidate(id session.SessionID)
- func (c *SessionMutationCapability) Remove(id session.SessionID)
- type SessionMutationClass
- type SessionMutationEntry
- type SessionProfile
- type SessionSummary
- type SessionTranscript
- type StorageHealth
- type StorageMaintenanceEvent
- type StorageMaintenanceState
- type StorageMaintenanceStatus
- type SuccessorPlacement
- type UserModelEntry
- type UserModelInspector
- type UserModelLister
- type WatchEnvelope
- type WorkspaceFactory
- type Worktree
- type WorktreeLister
- type WorktreeSelectorIssuer
Constants ¶
const ( // FeatureServerInfo is this RPC itself. It is degenerate over the wire — a // client that received a response already knows the server implements it — // but it is load-bearing as the registry's self-test: the feature set is // never empty, so "empty means something went wrong" stays a usable // assertion for every other consumer. FeatureServerInfo = "server_info" // FeatureWatchSessionEvents is the durable replay-then-follow watch — the // WatchSessionEvents RPC and its SSE peer (issue #821, ADR 0250). // // It answers "does this BUILD implement the watch?", which is the question a // client needs before it decides between one watch and the older // replay-then-subscribe dance. It deliberately does NOT answer "will a watch // succeed here": that additionally depends on the wired event log implementing // the cursor seam, which is a DEPLOYMENT fact and is reported by the // watch_unsupported error instead. // // The DISAMBIGUATOR is the stable `code` string, not the gRPC status. // watch_unsupported is registered as Unimplemented — exactly what grpc-go // returns for a method the server does not have — so a client switching on the // status alone still cannot separate a cursor-less store from version skew; it // has to read the code. That is the ADR 0248 design rather than a compromise: // the sibling no_event_log refusal ships the same status for the same class of // fact one level up, and moving this one to FailedPrecondition would make two // sibling refusals disagree while breaking clients whose Unimplemented handling // already covers it. What the feature flag buys is that a client never has to // reach the error at all to know whether the build has the RPC. FeatureWatchSessionEvents = "watch_session_events" // FeatureMCPServersOnCreate is client-provided MCP servers on session // creation — CreateSessionRequest.mcp_servers and its HTTP peer (issue #821, // ADR 0237). // // It is the FIRST listener-scoped feature, and the reason serverFeatures grew // a scope argument. Unlike its siblings it answers a DEPLOYMENT question, not // a build one: every build implements the field, and whether a given // deployment accepts it depends on whether any of its API listeners is a // network boundary. A client that reads this identifier learns "I may send // mcp_servers HERE" — which is the only useful form of the answer, since a // build-only claim would be true on a daemon that refuses every such request. FeatureMCPServersOnCreate = "mcp_servers_on_create" )
A feature identifier is an OPEN STRING, not an enum value.
This is the discipline the event taxonomy already settled on the same axis (AGENTS.md: EvNoProgress/StopNoProgress are "STRING passthroughs on the wire … no `task generate` needed"). A closed proto enum would make every added feature a wire-compat event requiring codegen and a proto review, and would leave an older client decoding new values as UNKNOWN while its exhaustive switch silently grew a dead branch. Adding a server feature should be a minor SDK release, not a schema change.
Identifiers are STABLE ONCE PUBLISHED. Renaming one is a break dressed up as a refactor: a deployed client gates on the exact string.
const ( // DefaultSessionInventoryPageSize applies when the caller omits page_size. DefaultSessionInventoryPageSize = 50 // MaxSessionInventoryPageSize is the hard response-row bound. MaxSessionInventoryPageSize = 100 )
const ( // WatchPhaseReplay marks a record that was already durable when the watch // attached. WatchPhaseReplay = "replay" // WatchPhaseLive marks a record appended while the watch was following, and // the ONE phase-only frame that announces the boundary between the two. WatchPhaseLive = "live" // WatchPhaseGap marks a position where a durable append is KNOWN to have // failed. // // It is a PHASE rather than an event kind, and that is the whole point of ADR // 0250 decision 5: a gap is a fact about DELIVERY, not something that happened // in the run. Making it a session.Event would leak it into the event taxonomy, // the proto Event message, the kind-parity gate, and every consumer that folds // events into a session. TestADR_0250_GapAddsNoEventKind asserts that absence // structurally, because "just add an EvGap so clients can render it" is a // natural-sounding change that would silently relocate a delivery concern into // the domain. WatchPhaseGap = "gap" )
The watch delivery phases (ADR 0250 decision 5). They are OPEN STRINGS on the wire, not an enum — the same discipline the event `type`/`stop` fields carry — so adding a phase is a minor SDK release rather than a wire-compat event, and an older client decoding a new value gets a string it can pass through rather than a dead branch in an exhaustive switch.
const APIMajor int32 = 1
APIMajor is the wire-contract major version this build speaks (ADR 0248).
It starts at 1 and bumps ONLY on a genuine break. Every additive change — a new RPC, a new field, a new capability — is announced through the feature vocabulary below instead, which is the entire reason that vocabulary exists. A client gates on APIMajor plus feature identifiers and NEVER on a server/package semver comparison: mecatl is deployed from main as often as from a tag, and a version string cannot tell a client whether a given RPC exists.
Variables ¶
var ( // ErrInvalidToken is the 401-class verdict: the credential is malformed, // unsigned, from the wrong issuer/audience, expired, not yet valid, or // otherwise not vouched for. A validator should wrap it. ErrInvalidToken = errors.New("invalid bearer token") // provider (its JWKS endpoint / discovery document) could not be reached, so // the token's validity is UNKNOWN. It is deliberately distinct from // ErrInvalidToken — an IdP outage must not be reported as an authn failure. ErrIdentityUnavailable = errors.New("identity provider unavailable") )
var ( // Wire adapters map it to Unimplemented / HTTP 501. ErrDreamUnavailable = errors.New("server: manual dream unavailable") // ErrDreamNotFound deliberately covers unknown, expired, and pre-restart plan IDs. ErrDreamNotFound = errors.New("server: dream plan not found; generate a new plan") // ErrDreamInProgress means the same authoritative decision is still applying. ErrDreamInProgress = errors.New("server: dream plan decision is still in progress") // ErrDreamConflict means an opposite decision cannot replace the applying decision. ErrDreamConflict = errors.New("server: dream plan has a conflicting decision in progress") // ErrDreamTerminalConflict means the plan already reached the opposite terminal decision. ErrDreamTerminalConflict = errors.New("server: dream plan was already decided differently") // ErrDreamCapacity means the bounded process-local review registry is full. ErrDreamCapacity = errors.New("server: dream review capacity exhausted") // ErrDreamGenerateFailed is the content-free generation failure category. ErrDreamGenerateFailed = errors.New("server: dream plan generation failed") // ErrDreamApplyFailed is the content-free partial/full application failure category. ErrDreamApplyFailed = errors.New("server: dream plan application failed") // ErrDreamDeadline is the content-free deadline category used by both transports. ErrDreamDeadline = errors.New("dream request deadline exceeded") // ErrDreamRequestFailed is the content-free fallback for unclassified backend errors. ErrDreamRequestFailed = errors.New("server: dream request failed") )
var ( // caller learns neither backend support nor aggregate storage scope. ErrManagementUnauthorized = errors.New("server: management authorization required") // maintenance cannot prove active-session exclusion in this deployment. ErrMaintenanceExclusionUnavailable = errors.New("server: storage maintenance exclusion unavailable") // ErrStorageHealthBackend is the sanitized aggregate-health backend failure. ErrStorageHealthBackend = errors.New("server: storage health unavailable") // ErrMigrationUnsupported reports that the configured session store has no // physical v1-to-v2 maintenance capability. ErrMigrationUnsupported = errors.New("server: session migration is not supported") // ErrMigrationConflict reports an invalid durable job transition. ErrMigrationConflict = errors.New("server: migration job conflict") // ErrMigrationBackend is the only caller-visible backend failure. Raw paths, // records, and backend error strings stay behind the adapter boundary. ErrMigrationBackend = errors.New("server: storage maintenance failed") // ErrCleanupPlanStale reports that catalog candidates, scope, or policy changed // after dry-run. No item is deleted from a stale plan. ErrCleanupPlanStale = errors.New("server: cleanup plan is stale") // ErrCleanupUnsupported honestly reports a backend without indexed pruning. ErrCleanupUnsupported = errors.New("server: session cleanup is unsupported") // ErrCleanupBackend is the sanitized stable maintenance failure. ErrCleanupBackend = errors.New("server: storage maintenance failed") // ErrStaleRunControl is returned when a control (approve / cancel / steer) // carries an expected_run_id that does NOT name the run it would affect // (ADR 0249). The control is refused and the current run is left untouched. // // It is a PRECONDITION-class failure, not a bad request: the request is // well-formed and the caller's belief was simply overtaken by events — the run // they meant to act on has already ended and another has begun. Adapters map // it to Aborted / HTTP 409 Conflict, alongside the other // you-lost-a-race sentinels (ErrMigrationConflict, ErrProposalConflict), so a // client can distinguish "retry against the current run" from "fix your // arguments". ErrStaleRunControl = errors.New("server: control targets a run that is no longer current") // ErrInvalidArgument signals a malformed or missing required field. ErrInvalidArgument = errors.New("server: invalid argument") // ErrNotFound signals an unknown session id. ErrNotFound = errors.New("server: session not found") // ErrTeamNotFound signals an unknown team id. It is distinct from ErrNotFound // (whose message names a session) so a team lookup reports a team-appropriate // message rather than "session not found: team X". Adapters map it to the same // codes.NotFound / HTTP 404 as ErrNotFound. ErrTeamNotFound = errors.New("server: team not found") // ErrChildNotFound signals a CancelChild for a child id the session's // in-flight run does not hold live — unknown, or already finished (the // finished-as-you-pressed race). The wording is FAMILY-NEUTRAL ("child // agent", never "subagent"): the same error will cover team-member and // parallel-branch ids once their cancel wiring lands. Distinct from // ErrNotFound (whose message names a session) so the HTTP /cancel-child // mirror reports a child-appropriate message; adapters map it to the same // codes.NotFound / HTTP 404. ErrChildNotFound = errors.New("server: child agent not found or already finished") // ErrNoMCPProvider signals that an MCP inspection RPC requiring a live // provider (ReadMcpResource / GetMcpPrompt) was called but no MCP provider // is configured. Adapters map it to FailedPrecondition / HTTP 412. ErrNoMCPProvider = errors.New("server: no MCP provider configured") // ErrFailedStepRetryIneligible is the stable precondition sentinel returned when a // session cannot retry its failed model step from persisted conversation state. // Eligibility is based only on typed persisted state. ErrFailedStepRetryIneligible = fmt.Errorf("%w: failed-step retry is not eligible", ErrFailedPrecondition) // ErrFailedPrecondition signals the request is well-formed but the server is // in a state that forbids it — typically a server-side misconfiguration the // client cannot fix by changing its arguments (e.g. spawning a Mutating team // member when no EnvironmentForker is wired, or a member catalog that violates // the read-only-share invariant). Adapters map it to FailedPrecondition / // HTTP 412, distinguishing it from a bad request (ErrInvalidArgument). ErrFailedPrecondition = errors.New("server: failed precondition") ErrLearningUnavailable = errors.New("server: learning proposals are not configured") // ErrAttemptVersionConflict reports an opaque expected-version CAS mismatch. ErrAttemptVersionConflict = errors.New("server: learning attempt version conflict") // ErrAttemptTerminalConflict reports a lifecycle state that cannot perform the requested control. ErrAttemptTerminalConflict = errors.New("server: learning attempt terminal conflict") // ErrAttemptLiveClaimConflict reports an attempt currently fenced by a live worker claim. ErrAttemptLiveClaimConflict = errors.New("server: learning attempt has a live claim") // ErrProposalConflict reports a stale proposal version or invalid lifecycle transition. ErrProposalConflict = errors.New("server: proposal conflict") // ErrInternal signals a server-side fault that is NOT the client's fault — a // transport/protocol error talking to a downstream (e.g. an MCP server that // is connected but errors a read). Adapters map it to Internal / HTTP 500, // distinguishing it from a bad request (ErrInvalidArgument). It exists so the // MCP read methods can keep an unknown-server name as InvalidArgument while a // genuine fault on a known server is reported as a server error. ErrInternal = errors.New("server: internal error") // ErrTooManySessionEngines is returned by createSession when the per-session // engine registry is already at Config.MaxSessionEngines. It bounds the memory // growth (CWE-770) from per-session engines created (by a client-MCP session OR a // non-default provider/model selector) but never released via CloseSession / // EndSession — the gRPC/HTTP surfaces have no connection-teardown drain, so a // hostile authed client could otherwise grow the map unbounded. Releasing a // session frees a slot. Adapters map it to ResourceExhausted / HTTP 429, mirroring // ErrTooManyTeams. ErrTooManySessionEngines = errors.New("server: too many live per-session engines") // ErrSessionLeasedElsewhere is returned by the run-entry funnel // (StartRunContent / resumeFromAwaiting) when a cross-process session lease // (cloud-native Phase 4, ADR 0027) for the id is held by a DIFFERENT, still-live // process: in a multi-replica deployment another replica owns this session, so // this one must NOT drive it (the single-writer invariant). It is the // composition-side surfacing of port.ErrLeaseHeld at the run-entry gate. // Adapters map it to FailedPrecondition / HTTP 409 Conflict — distinct from // ErrNoActiveRun: the session exists and is well-formed, it is just owned // elsewhere right now (a later retry, after the holder releases or its lease // lapses, can succeed). Only ever returned when a SessionLease is wired. ErrSessionLeasedElsewhere = errors.New("server: session is leased by another process") // StartRunContent + resumeFromAwaiting) when the server is DRAINING — it has // been asked to stop accepting new runs (mecak8s graceful shutdown, ADR 0048). // A drained run-entry is rejected before leasing/launching so a rolling update // steers new traffic to a survivor. In-flight runs are cancelled (not drained // to completion); a same-process Approve on a LIVE run is NOT a new run-entry // and stays allowed (it delivers a verdict to an already-running run). Adapters // map it to Unavailable / HTTP 503. The gate starts false (byte-identical // default); Service.Drain arms it. ErrUnavailable = errors.New("server: draining, not accepting new runs") // ErrNotAwaitingPlan is returned by ApprovePlan when the session is not parked // awaiting a PLAN-ORIGINATED permission ask (issue #206, Wave 4): either a run // is LIVE for the session (an approve mid-run — use the Converse ResumeApproval // frame for a live run), the session is not in StateAwaiting, or its pending // ask is a generic tool-permission ask rather than the plan-approval gate's // PresentPlan signalling call. It is a PRECONDITION failure (the session exists // and is well-formed, it is just not in the state this atomic RPC requires), // NOT a bad request. Adapters map it to FailedPrecondition / HTTP 409 Conflict // (distinct from ErrNoActiveRun's "known session, no live run" — here the // session may well be live, just not awaiting a plan ask). ErrNotAwaitingPlan = errors.New("server: session is not awaiting a plan approval") // Schedule-surface sentinels (internal/adapter/server/schedule.go). Defined // here so toStatus/writeServiceError map them in the one error-classification // chokepoint alongside the team/session sentinels. // ErrNoScheduleStore signals that no ScheduleStore is available — the // configured store backend does not expose one. Adapters map it to // Unimplemented / HTTP 501. ErrNoScheduleStore = errors.New("server: scheduled tasks are not supported by the configured store") // ErrNoEventLog signals that no durable EventLog (cloud-native Phase 3a, // port.EventLog) is configured — the configured store backend does not expose // one. It is the EventLog analogue of ErrNoScheduleStore: // StreamSessionEvents returns it so the wire adapters map to UNIMPLEMENTED // (HTTP 501), honestly reporting that the read-back surface is absent rather // than pretending an unknown id. ListSessions does NOT use it (it degrades to // an empty list via PrunableStore instead). ErrNoEventLog = errors.New("server: no durable event log configured") // ErrClientMCPUnsupported means this DEPLOYMENT does not accept // client-provided MCP servers on session creation (ADR 0237's listener-scoped // authority, applied to outbound MCP). It is the deployment's refusal, not the // build's: the RPC and the field exist, this deployment just does not offer // them, exactly as ErrNoEventLog reports a wired-storage fact one level up. // Both map to UNIMPLEMENTED / 501 for that reason, and a client that wants to // know BEFORE it asks reads mcp_servers_on_create from GetCompatibilityInfo. ErrClientMCPUnsupported = errors.New("server: client-provided MCP servers are not accepted on this deployment") // ErrClientMCPUnreachable means the deployment DID accept the request but at // least one requested MCP server could not be connected, so the session was // not created. It is the counterpart of ErrClientMCPUnsupported and a // deliberately DIFFERENT code: "this deployment refuses the field" is // permanent and a client should stop asking, while "your server did not // answer" is transient and retryable once the client's own endpoint is up. // Collapsing them into one code would make an SDK unable to tell a // misconfigured deployment from a sleeping sidecar. // // Creation is ALL-OR-NOTHING on the wire for the reason this sentinel exists: // a partially-mounted session is one the client cannot detect, since the // unreachable-server WARN goes to the operator's log and the create otherwise // returns a perfectly ordinary session id. ErrClientMCPUnreachable = errors.New("server: a requested client-provided MCP server could not be connected") // ErrSessionDeleteUnsupported means the configured store cannot physically // remove snapshots and their sidecars. ErrSessionDeleteUnsupported = errors.New("server: session deletion is not supported by the configured store") // ErrSchedulerNotRunning is returned by FireNow when a ScheduleStore IS // available (Create/Get/List/etc. all work) but no scheduler.Scheduler is // wired on this process (s.scheduler == nil — e.g. --scheduler was not // passed, or this is a store-only replica). It is distinct from // ErrNoScheduleStore (which means the STORE itself cannot hold schedules at // all): here the schedule exists and is well-formed, there is just no // in-process scheduler to drive a manual fire. Adapters map it to // FailedPrecondition / HTTP 412, the same class as ErrScheduleDisabled. ErrSchedulerNotRunning = errors.New("server: scheduler is not running") // ErrScheduleDisabled is returned by FireNow when the schedule is not enabled // (paused or done). It wraps scheduler.ErrFireNowDisabled. Adapters map it to // FailedPrecondition / HTTP 412. ErrScheduleDisabled = errors.New("server: schedule is disabled") // ErrFireNowOverlap is returned by FireNow when the singleton overlap check // found a prior fire still running. It wraps scheduler.ErrFireNowOverlap AND // port.ErrFireNowOverlap (the port-level sentinel a layer that may not import // this adapter — e.g. engine/agent's Schedule tool — matches via errors.Is, // the same create-seam default-true Singleton holding through every surface). // Adapters map it to FailedPrecondition / HTTP 412 (the schedule exists and // is well-formed, it is just running — the same precondition-failed class as // ErrScheduleDisabled; the two surfaces agree). ErrFireNowOverlap = fmt.Errorf("server: fire-now skipped (prior fire still running): %w", port.ErrFireNowOverlap) // ErrScheduleExhausted is returned by FireNow when a one-shot schedule has // already fired (FireCount > 0). It wraps scheduler.ErrFireNowExhausted. // Adapters map it to FailedPrecondition / HTTP 412. ErrScheduleExhausted = errors.New("server: one-shot schedule already fired") // ErrScheduleNotLeader is returned by FireNow when this replica is not the // scheduler leader (a multi-replica deployment where a peer holds the // `__scheduler__` lease). It wraps scheduler.ErrNotLeader. Adapters map it // to FailedPrecondition / HTTP 412; the message names the current leader // (when known) so a client can redirect. ErrScheduleNotLeader = errors.New("server: not the scheduler leader") )
Sentinel errors the service returns; the gRPC and HTTP adapters map these to their respective status codes (codes.InvalidArgument / NotFound, HTTP 400 / 404).
var ( // ErrInvalidPlacementSelection reports a malformed selector or binding request. ErrInvalidPlacementSelection = errors.New("server: invalid placement selection") // ErrPlacementNotFound deliberately covers absent and authorization-hidden selectors. ErrPlacementNotFound = errors.New("server: placement selector not found") // ErrPlacementStale reports an expired selector without disclosing hidden choices. ErrPlacementStale = errors.New("server: placement selector is stale") ErrPlacementUnavailable = errors.New("server: placement unavailable") // ErrPlacementChanged reports an inventory revision race during Bind. ErrPlacementChanged = errors.New("server: placement changed during bind") // ErrInvalidPlacementBinding reports unsafe or internally inconsistent provider output. ErrInvalidPlacementBinding = errors.New("server: invalid placement binding") )
var ErrActivityGap = errors.New("server: a durable event-log append failed; this watch has a delivery gap")
ErrActivityGap means a durable append failed while a watch was attached, so the watch's stream is known to be incomplete.
var ErrConfig = errors.New("server: invalid config")
ErrConfig is returned by NewService when a required dependency is missing.
var ErrNoActiveRun = errors.New("server: no active run for session")
ErrNoActiveRun is returned by Approve/Cancel when the session exists (possibly loaded from the store after a restart) but has no in-flight run in this process to deliver the control to AND nothing to resume. The session state is still loadable via GetSession.
Since cloud-native Phase 2, an Approve/Deny against a runless AWAITING session is NO LONGER ErrNoActiveRun: it re-enters the loop AT the ask and resumes (resumeFromAwaiting). ErrNoActiveRun therefore now means "the session exists but is in a terminal/idle state with nothing to resume" — idle, completed, cancelled, or failed. A failed/cancelled session recovers only through a NEW prompt (loadAndReopen → Recover/Interrupt), never through the approve seam. Cancel keeps the original meaning for every state (no live run to cancel).
var ErrTeamNotRunning = errors.New("server: team is not running")
ErrTeamNotRunning is returned when an operation requires a RUNNING team but the team's phase is teamCreated (created, never run) or teamDone (RunTeam already returned) — a CancelTeammate has no in-flight run to reach into. It maps to codes.FailedPrecondition.
var ErrTeamRunning = errors.New("server: team is already running")
ErrTeamRunning is returned when an operation is rejected because the team is already running: a second concurrent RunTeam, a SpawnTeammate after the team has started, or a CleanupTeam on a still-running team. It maps to codes.FailedPrecondition.
var ErrTeamsDisabled = errors.New("server: agent teams are not enabled")
ErrTeamsDisabled is returned by the team methods when no MemberEngine is wired in Config — agent teams are an opt-in capability the composition root enables.
var ErrTooManyTeams = errors.New("server: too many live teams")
ErrTooManyTeams is returned by CreateTeam when the live-team registry is already at Config.MaxTeams. It bounds the leak from teams created but never cleaned up. It maps to codes.ResourceExhausted.
var ErrWatchLagging = errors.New("server: watch terminated because the client fell behind the bounded delivery buffer")
ErrWatchLagging means a watch was terminated because its client fell behind the bounded delivery buffer for longer than the grace.
TERMINATING is the point (ADR 0250 decision 7). Service.Subscribe, the pre-cursor live registry, DROPS events for a slow subscriber: the stream stays open and the client never learns it is missing data. A durable cursor exists precisely so that the honest alternative is available — end the stream, and let the client resume from the last cursor it received, losing nothing.
It carries NO cursor, deliberately. The server's furthest-queued position is NOT the client's: envelopes still sitting in the delivery buffer were never received, so resuming from a server-side cursor would SKIP exactly the events the termination was supposed to protect. The client's own last-received envelope is the only correct resume point, and the client always has it.
var ErrWatchUnsupported = errors.New("server: the configured event log does not support cursors")
ErrWatchUnsupported means the configured durable EventLog does not implement port.CursorEventLog, so it cannot serve a positional resume or a follow.
It is deliberately an ERROR rather than a degrade to "replay the whole transcript and stop". A client that asked to resume from a position and was handed everything from the beginning is a correctness problem dressed as a performance one: it would silently re-process events it had already acted on, and never learn that its cursor meant nothing.
Functions ¶
func ClampInt32 ¶
ClampInt32 narrows a Go int (counter/index/count) to the proto int32 wire type, saturating at the int32 bounds rather than wrapping. These values (turn indices, counters, model counts) never realistically approach the limit; the clamp exists only so the conversion is provably overflow-safe. Exported so internal/app (e.g. the ProviderStatus model_count clamp) can reuse it instead of hand-rolling the same gosec G115 dance.
Written as the if-assign idiom, not `min(v, math.MaxInt32)`, because gosec's G115 range analysis tracks this bound but does NOT propagate it through the `min` builtin (the min form trips G115 on the int32 cast below).
func ClassifyAllBoundaries ¶
func ClassifyAllBoundaries() []error
ClassifyAllBoundaries runs every server-owned classified surface's guard and returns the concatenated findings — the single entry point TestInvariant_owned_access_is_classified drives (AC5.1).
func ClassifyCallerStoreBoundaries ¶
func ClassifyCallerStoreBoundaries() []error
ClassifyCallerStoreBoundaries walks every exported memory.CallerStore method (the cache/index boundary for caller-partitioned memory).
func ClassifyServiceBoundaries ¶
func ClassifyServiceBoundaries() []error
ClassifyServiceBoundaries walks every exported *Service method (the application-facade, in-memory-registry, and event-relay boundary) and reports one error per unclassified, misclassified, or stale entry.
func ClassifySystemBoundaries ¶
func ClassifySystemBoundaries() []error
ClassifySystemBoundaries walks every registered internal/syscaller.Root (the explicit shared-infrastructure system-principal scopes, ADR 0212 decision 5).
func DeriveTitle ¶
DeriveTitle returns the session's human-readable label: the snapshot Title if set, else the clamped text of the FIRST genuine user prompt found by walking sess.Conversation.Messages (via session.IsGenuineUserPrompt, which skips synthesised compaction summaries), else "" (no genuine prompt). It is the lazy display-time fallback for a session whose Title was never seeded (e.g. a session created before the Title field existed, or one whose first prompt was multimodal-only). It does NOT mutate sess.Title — NO write-on-read: the snapshot stays the authoritative set-once label, and the derived value is a pure read projection the caller places on the wire. Used by ListSessions (picker) and the GetSession handlers.
func NewScheduleManager ¶
func NewScheduleManager(cfg ScheduleManagerConfig) *scheduleManager
NewScheduleManager constructs a scheduleManager from the plain pre-Service inputs (ADR 0076): a port.SessionStore + a now-func ALONE (no *Service value required, resolvable before buildEngine). It resolves the ScheduleStore as follows: when ScheduleManagerConfig.ScheduleStore is non-nil (the --schedule-store-url composition override) it WINS — the registry is a remote driver, not the session store's own accessor, so the in-chat Schedule tool + the tick loop + the fire path share the ONE resolveScheduleStore resolution; otherwise the store is type-asserted for a ScheduleStore via the scheduleStoreProvider accessor (the jsonlstore + redisstore expose one). A store that backs no ScheduleStore AND carries no override (the in-memory memstore) yields a nil/absent manager — the honest no-scheduling path, matching ServerCapabilities.Scheduling. The returned manager satisfies port.ScheduleManager (all nine verbs) and is the single truth the *Service delegates to.
Models is OPTIONAL: a standalone-constructed manager (no Models pointer) admits only the empty selector (an empty inventory) — composition passes the Service's own pointer so SetModels keeps working with no second copy. Diagnostics is OPTIONAL and nil-safe.
func PlanManualRetention ¶
func PlanManualRetention(rows []port.SessionDiscoveryMeta, policy RetentionPolicy, owner *session.Principal, live, leased map[session.SessionID]bool, now time.Time) sessionretention.Result
PlanManualRetention runs the shared, side-effect-free retention planner (sessionretention.Plan) behind the manual cleanup API. internal/app's automatic sweep (childGC.sweep) calls sessionretention.Plan directly with the same policy/scope shape; see TestSessionStorageContinuity_Scenario5_AutomaticManualPlannerParity in internal/app/childgc_test.go for the cross-check between the two real call sites (AC5.5).
func PresentScheduleName ¶
PresentScheduleName projects the caller-visible name from an authoritative stored schedule. Ownerless records carry literal names byte-for-byte. Owned records carry an owner-qualified physical key, which is stripped only when it matches the exact namespace derived from the stored owner. Arbitrary strings never acquire physical-key provenance from their grammar alone.
func ValidateClassifiedNames ¶
func ValidateClassifiedNames(surface string, table map[string]ClassificationEntry, boundaries []string) []error
ValidateClassifiedNames compares actual boundary names with their classification entries. It is exported for composition-owned registries whose concrete registrations are not visible to this package.
Types ¶
type ACPEnvironmentOverlay ¶ added in v0.0.24
type ACPEnvironmentOverlay func(session.SessionID, tool.Environment) (tool.Environment, error)
ACPEnvironmentOverlay decorates an already-authorized placement environment with editor-buffer semantics. The returned environment must preserve the exact placement identity.
type AccessKind ¶
type AccessKind int
AccessKind is one of the four classifications ADR 0212 decision 2 requires for every designated application object-touching boundary.
const ( // KindCallerOwned means the boundary itself resolves the ownership // decision (ownsResource/authorizeSession/authorizeSchedule, or an // equivalent per-kind check such as memory.CallerStore's context-derived // namespace) on every call, denying a foreign caller as absence. // // MANAGEMENT-AUTHORITY SUB-CASE (the storage-maintenance boundaries: // StorageHealth, Plan/Apply/Resume/CancelSessionMigration, // SessionMigrationJob, Plan/Apply/CancelSessionCleanup, // SessionCleanupJob): the boundary still resolves a real per-caller // identity decision — "is this the verified management principal", and // for the ones that mint a job/plan/token, "does this handle belong to // THIS caller" — so KindCallerOwned's structural contract (a real ctx // check, no bypass) holds. But unlike an ordinary caller-owned boundary, // the underlying DATA these operate over is store-wide (every session), // never narrowed to "this caller's own rows". Don't read their entries' // "caller-owned" kind as data-scoping; read the rationale text for what // is actually decided. A future 5th AccessKind naming this sub-case // explicitly would need its own ADR (per ADR 0212's closed 4-kind // taxonomy) — not done here. KindCallerOwned AccessKind = iota // KindDerived means the boundary carries no independent decision of its // own: it operates on an identifier a caller can only obtain from an // ALREADY-classified caller-owned boundary (e.g. the id a CreateSession // call just returned, or an id a Cancel/EndSession call already // authorized before reaching it). It resolves ownership by construction, // not by re-checking. KindDerived // non-caller-identified operation — an internal system-principal root // (ADR 0204 decision 7) or a process-wide catalog/config read that is, // by design, the same for every caller. KindSharedInfrastructure // KindExempt is an explicit, reviewed carve-out for a boundary that // structurally cannot carry caller identity — a pure composition-time // wiring setter/accessor, a lifecycle control, or a workspace-path-scoped // (not caller-scoped) read gated by the pre-existing project-trust axis. KindExempt )
func (AccessKind) String ¶
func (k AccessKind) String() string
type ActivityGapError ¶ added in v0.0.22
type ActivityGapError struct{}
ActivityGapError is the process-local, GUARANTEED tier of ADR 0250 decision 6's three-tier append-gap guarantee: when an append fails, every watcher in the failing process terminates with this error and its cursor never advances.
Like ErrWatchLagging it carries no cursor, for the same reason: the client's last-received envelope is the resume point, and the server must not invent one that would skip the buffered tail.
The guarantee this belongs to is DELIBERATELY WEAKER than an absolute, and the weakness is not an implementation gap. A failed append consumed no position, so it leaves nothing for a watcher in ANOTHER process to observe; the best-effort durable gap marker (AppendGap) covers the likely case of one rejected record, and a total backend outage plus process loss leaves a gap that is undetectable by construction. Do not restate this as an absolute.
It carries NO description of the underlying failure, deliberately. The cause is a raw backend error — `dial tcp 10.0.0.5:6379: connect: connection refused`, a jsonlstore path — and this value reaches the client as a gRPC status message and an SSE `error` field. That is the same exposure GetSession already refuses under ownership enforcement, on the same reasoning: the store's error routinely embeds infrastructure detail a caller has no business reading. The cause is not lost — it goes to the durable gap marker (tier 1) and to the append-failure WARN the recorder already emits — so the operator keeps every byte of it and the client gets the stable `activity_gap` code, which is the whole of what it can act on.
func (*ActivityGapError) Error ¶ added in v0.0.22
func (*ActivityGapError) Error() string
func (*ActivityGapError) Unwrap ¶ added in v0.0.22
func (*ActivityGapError) Unwrap() error
Unwrap lets errors.Is(err, ErrActivityGap) classify this through the shared error registry, so both transports report it identically.
type ActivityReplayStatus ¶
ActivityReplayStatus describes the optional EventLog activity plane. EventLog has no completeness attestation, so Complete and Authoritative remain false.
type AuthenticationRejectionCategorizer ¶
type AuthenticationRejectionCategorizer interface {
AuthenticationRejectionCategory() string
}
AuthenticationRejectionCategorizer supplies a safe, closed diagnostic category for an authentication failure. Its value is never sent to clients.
type Authenticator ¶
type Authenticator struct {
// contains filtered or unexported fields
}
Authenticator bundles the configured auth + rate-limit policy and exposes the gRPC interceptors and HTTP middleware that enforce it. Construct it once and share it across both surfaces.
func NewAuthenticator ¶
func NewAuthenticator(cfg SecurityConfig) *Authenticator
NewAuthenticator builds an Authenticator from cfg. When rate limiting is disabled the limiter set is nil and the rate-limit checks are skipped.
func (*Authenticator) Close ¶
func (a *Authenticator) Close()
Close releases the edge's own long-lived resources at shutdown. Today that is exactly one thing: the configured validator's teardown, when it has one.
Teardown is an OPTIONAL CAPABILITY, type-asserted (the port.HookApprovalLearner idiom), never a method on PrincipalValidator — widening that single-method interface would break every fake and every test that scripts the seam. A validator without a Close needs none.
It exists because the real validator (toolhive-core/authn) owns a BACKGROUND JWKS refresh that its Close() stops, and cancelling the root context does NOT call Close(). Both server mains defer Authenticator.Close.
Safe to call more than once and on the identity-OFF zero value.
func (*Authenticator) Middleware ¶
func (a *Authenticator) Middleware(next http.Handler) http.Handler
Middleware wraps next with bearer-auth and rate-limit enforcement. The health endpoints (/healthz, /readyz) MUST be mounted outside this middleware so they remain reachable without credentials and are never rate limited.
func (*Authenticator) StreamInterceptor ¶
func (a *Authenticator) StreamInterceptor() grpc.StreamServerInterceptor
StreamInterceptor returns a grpc.StreamServerInterceptor enforcing auth then rate limiting before the stream handler runs. The rate check is applied once, at stream establishment.
func (*Authenticator) UnaryInterceptor ¶
func (a *Authenticator) UnaryInterceptor() grpc.UnaryServerInterceptor
UnaryInterceptor returns a grpc.UnaryServerInterceptor enforcing auth then rate limiting before the handler runs. The verified principal (if any) rides the handler context.
type CORSPolicy ¶ added in v0.0.22
type CORSPolicy struct {
// contains filtered or unexported fields
}
CORSPolicy is an EXACT-ORIGIN cross-origin policy for the HTTP API.
Exact means exact: an origin matches only if it is byte-equal (after normalisation) to a configured one. There is no wildcard, no suffix match, and no subdomain match. Suffix matching is the classic CORS bug — an allowlist of "example.com" matched by suffix also admits "evil-example.com" and "example.com.attacker.net" — and mecatl's HTTP API can start agent runs, so the cost of getting it wrong is not information disclosure but arbitrary action taken with the victim's credentials.
A nil *CORSPolicy is the DEFAULT and is a no-op: with no --cors-origins the middleware is not installed at all and responses are byte-identical to before this type existed.
This is the LOCAL-DEVELOPMENT path. The production browser path remains a same-origin BFF that injects bearer credentials server-side and enforces its own Origin/CSRF policy. See ADR 0248.
func NewCORSPolicy ¶ added in v0.0.22
func NewCORSPolicy(origins []string) (*CORSPolicy, error)
NewCORSPolicy validates and builds a policy from configured origins.
Validation is strict and happens at STARTUP, where the operator is present and the message is actionable. A malformed entry is refused rather than silently ignored: an ignored entry looks identical to a working one until a browser quietly fails, and the operator's mental model would be wrong in the unsafe direction.
func (*CORSPolicy) AllowedOrigins ¶ added in v0.0.22
func (p *CORSPolicy) AllowedOrigins() []string
AllowedOrigins returns the configured origins, sorted. It exists for startup logging and tests; the policy decision never walks this slice.
func (*CORSPolicy) Middleware ¶ added in v0.0.22
func (p *CORSPolicy) Middleware(next http.Handler) http.Handler
Middleware applies the policy to next.
IT MUST WRAP OUTSIDE THE AUTH MIDDLEWARE. A browser preflight is an unauthenticated OPTIONS request — the CORS specification forbids sending credentials on it — so a policy installed INSIDE auth would 401 every preflight and cross-origin access would never work at all. Wrapping outside is safe because a preflight is answered with headers only: it never reaches a handler, never touches a session, and never returns data.
type CapabilityReason ¶
type CapabilityReason string
CapabilityReason is a stable machine-readable explanation for a disabled inventory action.
const ( // CapabilityReasonInspectOnlyKind means the session kind is available for // inspection but cannot be driven through the public chat entry point. CapabilityReasonInspectOnlyKind CapabilityReason = "inspect_only_kind" // CapabilityReasonAwaitingApproval means the chat has an unresolved approval. CapabilityReasonAwaitingApproval CapabilityReason = "awaiting_approval" // CapabilityReasonActiveElsewhere means another live run currently owns the chat. CapabilityReasonActiveElsewhere CapabilityReason = "active_elsewhere" CapabilityReasonTranscriptUnavailable CapabilityReason = "transcript_unavailable" // CapabilityReasonStorageUnsupported means the configured store cannot perform the action. CapabilityReasonStorageUnsupported CapabilityReason = "storage_unsupported" // CapabilityReasonUnknown means the row cannot prove action eligibility. CapabilityReasonUnknown CapabilityReason = "unknown" )
type ClassificationEntry ¶
type ClassificationEntry struct {
Kind AccessKind
Rationale string
}
ClassificationEntry is one boundary's per-kind table row (ADR 0212 decision 2). Rationale is MANDATORY: a shared-infrastructure/exempt entry that cannot state a concrete, reviewable reason is rejected by validate, so an exemption can never become a silent caller-owned bypass (AC5.3).
type CleanupCandidate ¶
type CleanupCandidate struct {
ID session.SessionID
Kind session.SessionKind
State session.State
Reason string
ModifiedAt time.Time
EstimatedBytes int64
// contains filtered or unexported fields
}
CleanupCandidate is content-free dry-run metadata, ordered oldest-first.
type CleanupCounts ¶
type CleanupCounts struct {
Total int
ByKind map[string]int
ByState map[string]int
ByReason map[string]int
}
CleanupCounts reports bounded aggregate classification without content.
type CleanupItemError ¶
CleanupItemError is a stable sanitized partial failure.
type CleanupJob ¶
type CleanupJob struct {
ID string
State string
Processed int
Deleted int
Skipped int
Stale int
Failed int
Errors []CleanupItemError
}
CleanupJob is a caller-bound management projection.
type CleanupPlan ¶
type CleanupPlan struct {
Token string
JobID string
Available bool
Generation string
PolicyVersion string
Eligible []CleanupCandidate
EligibleCounts CleanupCounts
Protected CleanupCounts
EstimatedBytes int64
}
CleanupPlan is a read-only manual retention result.
type CleanupScope ¶
type CleanupScope struct {
Kinds []session.SessionKind
}
CleanupScope is the exact durable-kind subset a store-wide management plan covers.
type ClientMCPGrant ¶ added in v0.0.22
type ClientMCPGrant struct {
// contains filtered or unexported fields
}
ClientMCPGrant is a DECIDED client-MCP result: specs that have passed the shared classifier AND this deployment's policy gate. Only Service.ClientMCPFromWire mints a non-empty one, because specs is unexported — so the invariant is carried by the TYPE rather than by a doc comment asking callers to behave.
That matters because WithClientMCP and the CreateSession* entries are all exported: before this, any in-process caller (the scheduler, a future composition root, a later refactor of the ACP adapter) could construct the option from raw specs and bypass both the classifier and the gate. A ClientMCPGrant{} built outside this package is EMPTY, which is inert — the worst a bypass attempt achieves is a session with no client MCP, never an unvalidated mount.
func (ClientMCPGrant) IsEmpty ¶ added in v0.0.22
func (g ClientMCPGrant) IsEmpty() bool
IsEmpty reports whether the grant carries no servers — either because the request declared none, or because it is a zero value built outside this package.
type Clock ¶
Clock returns the current wall time. It defaults to time.Now when nil so the server can stamp session creation timestamps deterministically in tests.
type Command ¶
type Command struct {
// Name is the command's invocation name (without the leading "/").
Name string
// Description is a short, capped one-line summary for the palette.
Description string
}
Command is the surface-agnostic listing metadata for one slash command (name + short description), mirroring prompt.Command. The Service exposes its own type so the wire adapters and the composition seam (CommandLister) need not import the prompt domain package directly.
type CommandLister ¶
type CommandLister interface {
// List returns the commands discovered under root, de-duplicated by name and
// name-sorted, or an error on a genuine discovery fault.
List(ctx context.Context, root string) ([]Command, error)
}
CommandLister enumerates the slash commands available under a workspace root. It is the composition-injected discovery seam backing ListCommands: the composition root supplies an implementation that closes over the run-path command expander and the workspace factory, so the palette and the run path agree on which commands exist. It is read-only.
type Config ¶
type Config struct {
// BuildID is the composed binary build identity exposed by GetServerInfo only.
BuildID string
// ServerImplementation is the stable composition family exposed by GetServerInfo.
// NewService admits only [a-z][a-z0-9-]{0,63}; invalid values report "unknown".
// It must not identify an instance, deployment, topology, configuration,
// capabilities, or authentication.
ServerImplementation string
// ProviderEndpoint returns the configured endpoint for a provider already known
// to the caller and composition. It must not perform discovery, session/store or
// config reads, or other side effects; GetServerInfo sanitizes its result before
// every response boundary. nil and unknown providers report unavailable.
ProviderEndpoint func(providerID string) string
// Engine is the shared agent engine that drives every run. Required.
Engine *agent.Engine
// DebugSessionEngine builds dedicated no-filesystem debug-session engines.
// Nil disables creation and makes persisted debug sessions fail closed at
// rehydration rather than falling back to Engine or SessionEngine.
DebugSessionEngine DebugSessionEngineFactory
// DebugMCP reports that the debug factory can borrow selected direct tools from
// a configured global MCP manager.
DebugMCP bool
// Store persists and looks up sessions. Required.
Store port.SessionStore
// StorageManagementAuthorized gates process-wide storage health. A nil
// authorizer disables the management capability. It must be derived from the
// trusted request context, never request-supplied owner data.
StorageManagementAuthorized func(context.Context) bool
// LocalStorageMaintenanceSingleWriter is true only when composition has proved
// the store itself is private to this process (the in-process IsLive registry
// plus backend family locks are then sufficient). Management authorization is
// not such a proof. Any durable or otherwise shareable store must leave this
// false and wire a working SessionLease before destructive migration, cleanup,
// or automatic retention is advertised or run.
LocalStorageMaintenanceSingleWriter bool
// SessionLiveness carries process-local engine-owned child activity. Service's
// own runs map covers top-level runs; delegation children never enter that map,
// so destructive maintenance must consult both. Cross-process activity remains
// protected by SessionLease.
SessionLiveness port.SessionLiveness
// RetentionPolicy is the effective operator policy projected into health.
RetentionPolicy RetentionPolicy
// StorageMaintenanceStatus reports the shared retention/migration/cleanup lifecycle.
StorageMaintenanceStatus func() StorageMaintenanceStatus
// StorageMaintenanceUpdate receives sanitized lifecycle transitions. nil keeps
// maintenance APIs functional without process-wide health observability.
StorageMaintenanceUpdate func(StorageMaintenanceEvent)
// OwnershipEnforced is true only when the request edge has a verifier wired.
// Its zero value preserves the ownerless compatibility path. When enabled,
// create retries compare the verified issuer/subject pair before exposing an
// existing caller-selected ID, and the store must implement port.SessionCreator
// so no generated, forked, or scheduled session can overwrite an existing snapshot.
OwnershipEnforced bool
// ClientMCPOnCreate permits CLIENT-PROVIDED MCP servers on a session-creating
// API request (CreateSessionRequest.mcp_servers and its HTTP peer). It is a
// deployment/composition policy in the shape ADR 0237 requires, NOT an
// inference the server package makes from its own socket state.
//
// The zero value FAILS CLOSED: a Service built without an explicit grant
// refuses the field. That direction is deliberate — accepting an arbitrary
// outbound endpoint plus its auth headers from an API caller lends the server's
// ambient network authority to a remote principal, so a composition root that
// has not thought about it must not accidentally grant it. mecated derives it
// from listener topology (clientMCPOnCreateForListeners): permitted only on a
// UNIX-socket gRPC listener with HTTP disabled. An attacker-named endpoint
// carrying caller-supplied credentials lends greater ambient authority than
// ordinary loopback traffic.
//
// It gates the WIRE surface only. The in-process CreateSessionWithMCP /
// LoadSessionWithMCP entries are unaffected: their caller is the ACP adapter,
// which is a stdio peer of the operator's own editor and has no listener at
// all, so listener-derived policy is meaningless there.
//
// The SAME value drives the mcp_servers_on_create advertisement (FeatureScope),
// so a deployment cannot advertise what it will refuse.
ClientMCPOnCreate bool
// PlacementProvider is the deployment-owned atomic placement seam (ADR 0291).
// Bind authorizes creation/successor choices, Reattach resolves only an exact
// persisted EnvironmentRef, and ListWorktrees issues source-scoped ephemeral
// selectors. app.Build always supplies the trusted local default; alternative
// composition may supply one provider that owns worktree or remote placements.
// It is mandatory.
PlacementProvider PlacementProvider
// PlacementScope is the trusted deployment scope supplied to every provider
// Bind. It must be non-empty when PlacementProvider is configured.
PlacementScope PlacementScope
// RootAuthority mints a complete authority set for a newly composed root.
// A nil callback preserves host-managed legacy sessions; app.Build always wires
// this callback with its assembled catalog. Carryover forks copy their source
// authority instead of invoking it.
RootAuthority func(session.SessionKind) session.Authority
// DefaultMode is applied when a CreateSession request leaves mode
// unspecified. Defaults to session.ModeDefault when empty.
DefaultMode session.PermissionMode
// DefaultLimits are the stop limits applied to a session created without
// explicit limits. Because a zero Limits value DISABLES every stop condition
// by design in package session, the composition root injects non-zero
// defaults here so a default session is always bounded. Per-field: a request
// that supplies any non-zero limit field is taken as explicit and used as-is.
DefaultLimits session.Limits
// Now supplies the creation timestamp; defaults to time.Now.
Now Clock
// NewID allocates session ids; defaults to a crypto-random hex generator.
NewID IDGenerator
// MCPProvider exposes the connected MCP servers' resources/prompts to the
// catalog-level inspection RPCs. Optional and nil-safe: when nil, the list
// RPCs return empty and the read/get RPCs return ErrNoMCPProvider.
MCPProvider mcp.Provider
// MCPSources is the resolved MCP source inventory snapshot taken at startup.
// It backs ListMcpSources and ListToolHiveGroups when MCPSourceProber is nil;
// in that case both derive purely from this snapshot and perform no live
// discovery. May be empty.
MCPSources []source.SourceInfo
// MCPSourceProber, when non-nil, re-consults the resolved MCP sources on each
// ListMcpSources/ListToolHiveGroups call and returns a FRESH inventory — so a
// client refresh reflects CURRENT source status/diagnostics (e.g. a ToolHive
// workload that crashed or appeared after startup), not the startup snapshot.
// It is the live-discovery seam: the composition root supplies a prober that
// closes over the resolved []source.Source and re-runs source.InspectSources.
// When nil, ListMcpSources falls back to the cached MCPSources snapshot. The
// prober is read-only (streaming-HTTP / container queries only; never spawns a
// process) and fail-soft: on any failure the Service falls back to the cached
// snapshot so the panel always renders.
MCPSourceProber func(ctx context.Context) []source.SourceInfo
// Commands lists the available slash commands for a workspace, backing the
// ListCommands RPC (the client's in-input command palette). It is the
// composition-injected discovery seam: the composition root (internal/app)
// closes over the SAME command expander it builds for the run path and the
// workspace factory, so the palette offers exactly the commands a "/<cmd>"
// prompt would expand. Optional and nil-safe: when nil (command expansion
// disabled, or no expander enumerates), ListCommands returns an empty list.
// It is read-only and called per request (discovery is cheap file scanning).
Commands CommandLister
// collaborators were assembled for. Every filesystem-capable placement with a
// different root must use SessionEngine, including custom environment kinds and
// rootless shared deployments.
SharedEngineRoot string
// Agents is the resolved agent-definition snapshot taken at startup. It backs
// ListAgents and is a pure read of this snapshot (no live discovery). The
// composition root (internal/app) resolves the registry once and projects each
// def into the proto form (name/description/resolved model/effective read-only
// tool scope/permission mode/color) so the server adapter never imports the
// agents adapter. May be empty (agent definitions disabled or none found).
Agents []*mecatlv1.AgentInfo
// Models is the resolved selectable-model inventory snapshot taken at startup
// (multi-provider Phase 0, S3). It backs ListModels and is a pure read of this
// snapshot (no live discovery — the registry's available providers + the
// embedded catalog are both fixed for the process lifetime). The composition
// root (internal/app) joins the registry's AVAILABLE providers to the catalog
// and projects each model into the proto form (modelSnapshot) so the server
// adapter never imports providercatalog or the registry. May be empty (zero
// providers available). NO secret material (no key, env var name, or base URL).
Models []*mecatlv1.ModelInfo
// DefaultCapabilities is the NEUTRAL per-(default provider+default model) input
// capability — the catalog ∩ adapter INTERSECTION computed once in composition
// (internal/app modelCapability for the registry default + cfg.Model). It is the
// single source for BOTH the shared/default-engine session_capabilities echo
// (when a session uses no per-session engine) AND ProviderCapabilities() (the ACP
// gate). The server adapter holds only this neutral value — it never imports the
// catalog or registry. The zero value (text-only) is the safe default for a
// child/member service with no provider. (multi-provider Phase 0, S5.)
DefaultCapabilities port.ProviderCapabilities
// Posture is the SERVER-WIDE operator posture-ladder tier as a string
// ("strict"/"trusted"/"auto"/"yolo"), projected into the ServerCapabilities echo
// as CHROME ONLY (a client renders a "⚠ auto"/"⚠ yolo" badge). It is NOT session
// state — it never changes per session; the per-session knob is permission MODE.
// Empty (the zero value / an unconfigured child service) yields no badge. String
// passthrough — no enum on the wire (the EvNoProgress/StopBudget discipline).
Posture string
// DeploymentID is an optional, opaque, operator-set label for this deployment,
// surfaced on GetServerInfo. It is empty by default and is NEVER derived from
// hostname, pod name, or environment: infrastructure topology is not something
// an authenticated caller is owed, and a label the operator did not choose is a
// leak with no consenting author. Bounded and validated at the composition
// root (mecated --deployment-id), not here. See ADR 0248.
DeploymentID string
// DefaultResolvedModel is the EFFECTIVE provider+model the DEFAULT/shared engine
// resolved to (the registry default provider + cfg.Model + the default context
// window), computed once in composition. It is the single source for the
// resolved_model echo of a session that uses no per-session engine, the SAME
// composition-computed single-source discipline as DefaultCapabilities — the
// server holds only this value and never recomputes the resolution in a handler.
// The zero value (empty ids) is the safe default for a child/member service with
// no provider; a client maps it to a no-model-segment header.
DefaultResolvedModel ResolvedModel
// DefaultModelPending is true when the DEFAULT/shared engine booted with an
// UNRESOLVED default model (issue #262 review finding 1: the sole
// intent-driven — ToolHive gateway — provider probed down at Build, so
// cfg.Model stayed ""). It routes EVERY zero-selector session through the
// per-session engine factory (sessionNeedsPerFactory) and, for a session
// persisted before a restart into a still-down proxy, through rehydration
// (needsRehydration) too — so the model is resolved AT SESSION-BUILD TIME
// (resolve-at-use, mirroring the Deps.ContextWindow precedent, but at
// session granularity) instead of being frozen at the shared engine's
// Build-time construction. Without this, a post-boot heal
// (registry.healDefaultModel) updates the registry's resolved default but
// never reaches a zero-selector session, which keeps sending an empty
// model id to the provider (the R1.4 "no-restart" promise silently broken
// for the flagship sole-provider case). Static for the process lifetime
// (set once in composition from the SAME condition healDefaultModel guards
// on: an intent-driven default provider with no resolved model at Build);
// false everywhere else (a keyed default, or an operator-configured
// --model/--default-model) — byte-identical to today.
DefaultModelPending bool
// Skills is the resolved skills-inventory snapshot taken at startup. It backs
// ListSkills and is a pure read of this snapshot (no live discovery — skills
// are discovered once at build time and immutable for the process lifetime).
// The composition root (internal/app) discovers the skills once and projects
// each into the proto form (name + description); that PROJECTION (skillSnapshot)
// lives in internal/app, not here, so the server adapter holds only the proto
// snapshot and never reaches into the skills adapter's discovery types. May be
// empty (skills disabled or none found).
Skills []*mecatlv1.SkillInfo
// Soul is the resolved soul (persona) BUILD-TIME SNAPSHOT taken at startup. It
// backs GetSoul and is a pure read of this snapshot — the soul is selected once
// (USER-wins precedence, project trust gate, drift check) and is immutable for the
// process lifetime, so no live re-read is warranted. The composition root
// (internal/app) projects the winning soul's content + soulMeta into the proto form
// (soulSnapshot) so the server adapter never reaches into the soul adapter or the
// composition-layer soulMeta type. nil when no soul source is wired (--no-soul or
// none present); a nil Soul makes capabilities().Soul false and GetSoul return an
// empty (present=false) snapshot.
Soul *mecatlv1.SoulInfo
// UserModel lists the CURRENT user-model entries, backing GetUserModel. Unlike
// Soul (a startup snapshot) it is a LIVE lister: the composition root closes over
// the user-model store's Index so a refresh reflects entries saved since startup.
// It is the same seam idiom as Commands. Optional and nil-safe: when nil (user
// model disabled) capabilities().UserModel is false and GetUserModel returns empty.
UserModel UserModelLister
// ReflectSession enables explicit completed-session reflection independently of
// automatic learning mode. Attempts expose only content-free lifecycle
// projections from the verified caller's private partition. Proposals and the
// mutation callbacks expose the bounded, caller-partitioned staged-learning
// review surface.
ReflectSession ExplicitReflector
Attempts learning.AttemptRepository
AttemptPrincipal func(*session.Principal) string
Proposals learning.ProposalRepository
ProposalPrincipal func(*session.Principal) string
PromoteProposal ProposalPromoter
UndoProposal ProposalUndoer
ProjectPromotionAllowed func(project string) bool
ProposalActionAvailable func(project string) (bool, string)
// DreamReviewer is the transport-neutral, process-local manual consolidation
// coordinator. DreamCapabilities is the composition-computed availability
// snapshot for the exact Build-owned project-memory and user-model stores. Both
// zero values keep manual dreaming unavailable.
DreamReviewer DreamReviewer
DreamCapabilities DreamCapabilities
// LearnedSkills exposes caller-partitioned, agent-owned lifecycle records. The
// publisher atomically refreshes the shared live Skill catalog after mutations.
LearnedSkills learning.SkillRepository
PublishLearnedSkills func(context.Context, learning.SkillPartition) error
BeginSkillPublication func(learning.SkillPartition) func()
LiveSkillGeneration func(learning.SkillPartition) uint64
SkillActionAvailable func(learning.SkillPartition, string) (bool, string)
LearnedSkillNameAvailable func(string) bool
LiveSkills func(context.Context) []*mecatlv1.SkillInfo
// SessionEngine builds a PER-SESSION engine over a non-default provider/model
// selector AND/OR client-provided streaming-HTTP MCP servers (the ACP
// session/new mcpServers). It is the seam that lets a session bind its OWN
// provider/model or mount its OWN MCP tools without leaking them (or the
// provider registry) into the shared Engine every other session uses. When nil,
// a non-default selector or non-empty MCP specs are rejected with
// ErrInvalidArgument; a session with the zero selector and no client MCP always
// uses the shared Engine (zero overhead). The composition root (internal/app)
// supplies it.
SessionEngine SessionEngineFactory
// ModeNeedsEngine reports whether a given session PermissionMode resolves a model
// that DIFFERS from the shared engine's model (ADR 0030 Layer 3) — i.e. whether a
// plan slot is configured and active. It is the composition-injected predicate that
// lets a DEFAULT-FS session (which normally rides the shared engine, zero overhead)
// be PROMOTED to a per-session factory engine when its mode would change the model.
// The Service never resolves a model itself; it asks this predicate.
//
// When nil (no plan slot configured, or a deployment that predates Phase 3) a
// default-FS session is NEVER promoted — BYTE-IDENTICAL to pre-Phase-3 behaviour
// (a mode flip changes nothing, the shared engine is unchanged). This is the
// regression-guard seam: composition wires it ONLY when a plan slot is active.
ModeNeedsEngine func(mode session.PermissionMode) bool
// MemberEngine builds a team member's Engine from the shared team and the
// member spec (see engine/agent.MemberEngine). It is the seam that wires
// the agent-team RPCs: when nil, those RPCs return ErrTeamsDisabled. The
// composition root supplies it (internal/app), capturing the per-member
// catalog (read-only base + MemberTools, plus mutating tools only for a
// Mutating member) and the provider/model.
MemberEngine MemberEngineFactory
// TeamGoalUntrusted, when true, re-fences the gRPC/HTTP CreateTeam goal as
// UNTRUSTED data in member and synthesis prompts (threaded to
// agent.WithUntrustedGoal). DEFAULT false: the goal is the team's TRUSTED
// top-level instruction (its provenance is the deployment/operator that owns the
// gRPC front door, not a peer — peer messages and task descriptions stay fenced
// regardless). A multi-tenant / relay deployment that interpolates untrusted
// end-user text into the goal should set this true so the goal is fenced as data.
// It is a composition decision (the deployment knows the goal's provenance); the
// supervisor only takes the bool.
TeamGoalUntrusted bool
// TeamTokenBudget is the team-wide cumulative token budget threaded into every
// CreateTeam supervisor (agent.WithTeamTokenBudget). 0 disables. The per-request
// proto knob is deferred.
TeamTokenBudget int
// Forker isolates a Mutating team member's workspace (force-copy: own `.git`).
// Optional; required only if a Mutating member is spawned.
Forker tool.EnvironmentForker
// ReadOnlyForker isolates a read-only-isolated team member's workspace as a cheap
// git worktree (shares the base repo's `.git` ⇒ full history) so an inspect-only
// member can run a shell (git log/show, build, test) confined to a throwaway
// checkout. Optional; required only if the member factory marks any read-only
// member IsolateReadOnly (which the composition root does only when this is
// wired). When nil, read-only members base-share with no shell.
ReadOnlyForker tool.EnvironmentForker
// read-only member (the no-shell fallback tier) so it never inherits a relaxed
// base's out-of-root reach (the path-escape-posture Scenario 5 boundary —
// threaded to agent.WithTeamSharedBaseWorkspace). The composition root wires it
// whenever the Workspaces factory may return a relaxed workspace (auto/yolo).
// Optional; nil keeps the historical verbatim base share.
SharedBaseWorkspace func(root string) tool.Workspace
// TeamHooks fires the team lifecycle hooks (TeammateIdle) and is passed to
// member coordination tools for the TaskCreated / TaskCompleted gates.
// Optional.
TeamHooks port.HookRunner
// MaxTeams caps the number of live (un-cleaned) teams the registry holds at
// once, bounding the leak when clients create teams but never CleanupTeam.
// CreateTeam returns ErrTooManyTeams (ResourceExhausted) when the cap is
// reached; cleaning up a created/done team frees a slot. Defaults to
// defaultMaxTeams when zero.
MaxTeams int
// MaxSessionEngines caps the number of live (un-released) PER-SESSION engines
// the registry holds at once (CWE-770). A per-session engine is registered when
// a session needs a non-default provider/model selector OR client-provided MCP
// servers. The ACP surface drains them on editor disconnect, but the gRPC/HTTP
// surfaces have no teardown signal, so without a cap a hostile authed client
// could call CreateSession with a valid provider_id repeatedly (never closing)
// and grow the map unbounded. createSession returns ErrTooManySessionEngines
// (ResourceExhausted) when the cap is reached; CloseSession / EndSession frees a
// slot. It is a generous count (a session is multi-turn and its engine MUST
// persist across turns, so this is NOT terminal-state eviction). Defaults to
// defaultMaxSessionEngines when zero.
MaxSessionEngines int
// OnCloseSession, when non-nil, is invoked by CloseSession with the closing
// session id BEFORE the per-session engine teardown. It is the composition
// seam for releasing session-scoped state the Service does not own — currently
// the per-session LEARNED permission rules (issue #3), evicted via
// permstore.Memory.Forget so they do not outlive the session. Optional and
// nil-safe.
OnCloseSession func(session.SessionID)
// EventLog durably records the relay-side event projection per session
// (cloud-native Phase 3a). Relays observe every event, including the
// drain-to-discard tail, while their run-scoped recorders coalesce streaming
// text deltas before Append; the loop itself stays storage-agnostic (it only
// emits). Optional and nil-safe: when nil the relay records nothing
// (byte-identical to the pre-3a behaviour). The composition root wires the
// durable jsonlstore Store (which also implements EventLog) or an in-memory
// sibling when no store dir is configured.
EventLog port.EventLog
// Diagnostics is the operational logging sink the relay uses to WARN once per
// recorder after EventLog.Append failures (a best-effort durable log must not
// break the live stream). Optional and nil-safe: when nil, Append failures are
// silently tolerated (the durability gap is the only effect). The composition
// root supplies the same sink the rest of the build uses.
Diagnostics port.Diagnostics
// ReplayApprovals repopulates the in-memory learned-rule store (permstore) for a
// loaded session from its durable EventLog allow-always verdicts (cloud-native
// Phase 3b). It is the consumer that kills the Phase 2 re-ask wart: the permstore
// is in-memory and lost on restart, so a previously allow-always'd tool would
// otherwise re-ask after a process restart. The composition root (internal/app)
// supplies the closure — it owns BOTH the EventLog and the Policy.Learn seam, so
// it reads the verdicts, correlates each allow-always askID back to its ToolCall
// in the loaded conversation (the askID encodes the call id; see agent.newAskID),
// and re-Learns the reconstructed rule. Keeping the correlation in composition
// keeps the EventLog event METADATA-ONLY (no raw args on the wire) while the real
// rule is rebuilt from history the session already carries (no leak).
//
// The Service invokes it from loadAndReopen (the single run-entry funnel) AT MOST
// ONCE per session id per process: a freshly-created in-memory session learns
// live, so it never needs a replay, and a re-run of an already-replayed session
// would only re-derive idempotent rules. Optional and nil-safe: when nil (no
// store, or replay not wired) loadAndReopen does nothing extra.
ReplayApprovals func(ctx context.Context, sess *session.Session)
// ResolveContextWindow is THE live-first context-window resolver injected by
// composition (the SAME reg.windowResolver the engine reads via Deps.ContextWindow,
// wrapped to int64: override→live→catalog→128k floor). Service.ResolvedModel
// consults it for BOTH the default-session and the per-session-engine branch, so
// the wire echo is byte-identical to the engine's resolve-at-use window. nil keeps
// the baked DefaultResolvedModel.ContextWindow verbatim — the memstore/driver/test
// paths. Only the ContextWindow scalar is resolved; provider/model identity never
// recomputes.
ResolveContextWindow func(providerID, modelID string) int64
// SessionLease is the OPTIONAL cross-process single-writer seam (cloud-native
// Phase 4, ADR 0027). When wired, the run-entry funnel acquires a per-session
// lease (AFTER the same-process runEntryMu, so same-process exclusion stays
// cheap) before driving the engine, refreshes it from a Service-owned renewer
// goroutine, and releases it on CloseSession / shutdown. A competing process
// holding the lease makes StartRunContent / resumeFromAwaiting fail with
// ErrSessionLeasedElsewhere. Optional and nil-safe: when nil there is NO
// acquire, NO renewer, and NO release — byte-identical to the pre-Phase-4
// single-writer-by-affinity posture. The loop NEVER imports port.SessionLease;
// the renewer and the held-lease registry live entirely on Service (the same
// storage-agnostic discipline as EventLog). A backend that reports
// ErrLeaseUnsupported is stickily disabled (one INFO, then the no-lease path).
SessionLease port.SessionLease
// MutationCapability is the process-local admission gate shared by Service
// and the engine's guarded persistence/recorder adapters. Composition supplies
// one instance to both. When nil, Service creates the matching local gate;
// no-lease and unsupported-lease paths remain pass-through.
MutationCapability *SessionMutationCapability
// LeaseOwner is this process's owner-identity string for SessionLease, built
// once per Build (e.g. "<hostname>-<pid>-<nonce>") so two Builds in one
// process get distinct owners. Ignored when SessionLease is nil.
LeaseOwner string
// LeaseTTL is the lease lifetime requested at Acquire and the renew window;
// the renewer ticks at LeaseRenewInterval (default LeaseTTL/3). A non-positive
// value defaults to defaultLeaseTTL. Ignored when SessionLease is nil.
LeaseTTL time.Duration
// LeaseRenewInterval is how often the renewer refreshes a held lease. A
// non-positive value defaults to LeaseTTL/3. Ignored when SessionLease is nil.
LeaseRenewInterval time.Duration
// Scheduler is the OPTIONAL in-process scheduled-tasks tick loop (issue #189,
// Phase 1f). When wired, NewService stores it on the Service so Close drains it
// (Stop cancels the tick loop + joins in-flight fires) and Drain arms its drain
// gate (no new fires mid-tick during shutdown). The scheduler is STARTED by
// composition (app.Build) AFTER NewService — its FireFunc closes over the
// Service, so Build calls SetFire then Start; NewService does NOT start it.
// nil = no scheduling (the byte-identical default).
//
// NOTE: this field is the LEGACY late-attach path. The schedule surface now
// lives on the store-shaped scheduleManager (ADR 0076) — see ScheduleManager
// below. NewService still seeds the Service's manager-side scheduler reference
// from this field for byte-identical Close/Drain; composition attaches the
// scheduler via SetScheduler (delegated to the manager) AFTER NewService.
Scheduler *scheduler.Scheduler
// ScheduleManager is the pre-Service store-shaped schedule manager (ADR 0076):
// the validated create/read/update/fire seam constructed BEFORE buildEngine
// from the store + now-func, with the scheduler / model-inventory as
// late-bound atomic fields. When non-nil, the Service delegates its nine
// port.ScheduleManager methods + EmitScheduleEvent + GetFire to it — the RPC
// surface is byte-identical. When nil, the Service self-constructs one from
// Store (the legacy test path + any caller that does not pre-construct); a
// store that backs no ScheduleStore yields a nil manager (the honest
// no-scheduling path). Composition (app.Build) constructs the manager from
// the store before buildEngine and hands it here — the SAME manager its
// shared catalog's Schedule tool factory resolves (one manager, one
// truth). The field is the CONCRETE *scheduleManager (exposed to
// composition as the ScheduleManagerImpl alias) so the typed-nil
// discipline holds end-to-end: a store with no ScheduleStore yields an
// untyped nil here, never a non-nil interface boxing a nil pointer.
ScheduleManager *scheduleManager
// PlanModeAutoApprove is the OPT-IN, OPERATOR-TIER-ONLY, DEFAULT-OFF flag that
// auto-approves a plan-mode PresentPlan ask when the run ends without a human
// operator. It is a deliberate autonomous-approval capability — an operator
// deployment decision, NEVER load-bearing for safety. When true AND the engine
// is headless (no interactive client attached), the Service auto-resolves a
// parked plan-approval ask via the EXISTING ApprovePlan path (ModeDefault + a
// loud note). It does NOT fire when interactive (a human can approve), NOT in
// non-plan modes, NOT for non-plan asks. DEFAULT false (the existing safe
// default: headless plan ask is auto-denied by the engine). Composition
// (internal/app) sets this from Config.PlanModeAutoApprove; the cmd mains wire
// the --plan-mode-auto-approve flag.
PlanModeAutoApprove bool
// Interactive reports whether a HUMAN approver is attached to the main engine's
// runs (a live Converse / HTTP-SSE client that can answer a permission ask).
// It mirrors app.Config.Interactive (threaded onto the engine's Deps.Interactive)
// and gates the PlanModeAutoApprove observer: an interactive deployment NEVER
// auto-approves (the human answers). DEFAULT false (fail-safe: the observer
// treats the deployment as headless).
Interactive bool
}
Config wires the server adapter to the WP8 engine and its collaborators.
type CreateSessionOption ¶
type CreateSessionOption func(*createSessionOpts)
CreateSessionOption is a variadic option applied to a CreateSession* call (the Go options idiom — NOT a method-signature widening). The only option today is WithSessionID, which lets a caller (the scheduler fire path) mint a session under a CALLER-chosen id instead of the Service's NewID generator. Unknown options from future callers are a no-op.
func WithClientMCP ¶ added in v0.0.22
func WithClientMCP(grant ClientMCPGrant) CreateSessionOption
WithClientMCP mounts client-provided streaming-HTTP MCP servers for the new session's lifetime, via a per-session engine (which requires Config.SessionEngine, else ErrInvalidArgument).
It takes a ClientMCPGrant, not raw specs: the grant is unforgeable outside this package, so "validated and policy-checked" is a type-level fact rather than a convention. This option does not validate and must not — one classifier, one gate, at the wire seam.
It also arms the ALL-OR-NOTHING mount requirement (clientMCPStrict): every requested server must actually connect or the create fails. That is the wire contract, and this option is the wire's only entry, so the two travel together rather than as a separate flag a handler could forget. The ACP path (CreateSessionWithMCP) deliberately does not come through here and keeps composition's best-effort behaviour.
An EMPTY grant is a no-op: it arms nothing, so a caller that passes a zero value gets the ordinary shared-engine create rather than a strict-mode session with nothing to mount.
func WithDebugMCP ¶ added in v0.0.22
func WithDebugMCP(names []string) CreateSessionOption
WithDebugMCP selects already-configured server-global MCP servers for a debug session. Names are validated at the create boundary and no connection details cross this seam.
func WithDebugTarget ¶ added in v0.0.22
func WithDebugTarget(id session.SessionID) CreateSessionOption
WithDebugTarget creates a dedicated debug session bound to id. The target is authorized through the ordinary absence-shaped ownership seam and is only loaded for validation; its state and conversation are never changed or copied.
func WithOwner ¶
func WithOwner(p *session.Principal) CreateSessionOption
WithOwner overrides the owner a CreateSession* call stamps on the new session (ADR 0204 decision 4). By DEFAULT the owner comes from the verified principal on the context (session.PrincipalFromContext) — a caller can never name its own owner in the request body, which is why CreateSessionRequest has no owner field. This option is the in-process injection seam for a caller that already holds the owning principal out of band: the scheduler fire path, which runs under the system principal but must attribute the fire session to the SCHEDULE's captured owner.
WithOwner(nil) is the EXPLICIT ownerless injection (a system-owned session with nobody to attribute it to) and does NOT fall back to the context principal — a fabricated owner is worse than none.
func WithPlacementBinding ¶ added in v0.0.24
func WithPlacementBinding(binding PlacementBinding) CreateSessionOption
WithPlacementBinding supplies a trusted exact binding already reauthorized by composition. It is intended for scheduled fire only; public transports cannot construct or select it.
func WithScheduledRelationship ¶
func WithScheduledRelationship(scheduleName string, origin session.SessionID) CreateSessionOption
WithScheduledRelationship is the trusted composition-only creation seam for scheduler fires. No public request field maps to this option.
func WithSessionID ¶
func WithSessionID(id session.SessionID) CreateSessionOption
WithSessionID overrides the session id a CreateSession* call mints. When set, the id MUST be non-empty and MUST NOT collide with a live per-session engine (the sessionEngines map); a collision is rejected with ErrInvalidArgument. An empty id is rejected. When no WithSessionID option is passed, the existing NewID path is byte-identical. It is the seam ADR 0059 decision #7 Phase-2 uses to mint "sched--"-prefixed fire-session ids.
func WithSourceSession ¶
func WithSourceSession(id session.SessionID) CreateSessionOption
WithSourceSession seeds a NEW session's conversation history from the named source session (issue #20: model-switch context carryover). The source is loaded through the run-entry funnel (loadAndReopen recovers terminal states to idle), snapshotted via session.ForkSnapshot (a deep copy with trailing unanswered tool calls stripped), and seeded into the new session BEFORE its first Store.Save via session.SeedHistory. Carryover is ALWAYS allowed across providers: a SAME-provider carryover replays the history verbatim (blobs intact, warm cache); a CROSS-provider carryover seeds a provider-neutral copy (session.StripProviderState clears Reasoning/ProviderPhase/ItemID). A source that is still running/awaiting is rejected with ErrFailedPrecondition. Empty (no option) is the byte-identical no-carryover path.
type DebugSessionEngineFactory ¶ added in v0.0.22
type DebugSessionEngineFactory func(ctx context.Context, sel ProviderSelector, profile SessionProfile, mode session.PermissionMode, target session.SessionID, targetFingerprint string, targetOwner *session.Principal, selectedServers, toolCeiling []string) (SessionEngineResult, error)
DebugSessionEngineFactory builds the dedicated engine for a debug session. targetFingerprint and targetOwner bind every evidence/MCP read to the exact authorized target incarnation; neither may be projected to the model or wire.
type DreamCapabilities ¶
type DreamCapabilities struct {
Generate bool
Decide bool
Targets map[DreamTarget]DreamTargetCapability
}
DreamCapabilities is the honest process-wide manual-review capability snapshot. Reasons are fixed operator-facing categories and never contain memory or provider content.
type DreamDecision ¶
type DreamDecision string
DreamDecision is the complete manual-plan decision vocabulary.
const ( // DreamDecisionApply applies the exact retained authoritative plan. DreamDecisionApply DreamDecision = "apply" // DreamDecisionDismiss retires the plan without mutating memory. DreamDecisionDismiss DreamDecision = "dismiss" )
type DreamOperation ¶
type DreamOperation struct {
Kind string
Survivor DreamParticipant
Sources []DreamParticipant
Replacement DreamReplacement
Reason string
ExactDuplicateEligible bool
}
DreamOperation is one ordered operation. It deliberately contains no versions, paths, provider/model identity, or caller-supplied target information.
type DreamParticipant ¶
DreamParticipant is reviewable memory content for one survivor or source.
type DreamReceipt ¶
type DreamReceipt struct {
ID string
Target DreamTarget
Disposition DreamDecision
Planned int
Applied int
Conflicted int
Skipped int
Failed int
}
DreamReceipt reports source counts supplied by the core dream.Report.
type DreamReplacement ¶
DreamReplacement is the proposed survivor content after synthesis.
type DreamReview ¶
type DreamReview struct {
ID string
Target DreamTarget
ExpiresAt time.Time
Operations []DreamOperation
PlannedOperations int
PlannedSourceCount int
}
DreamReview is the detached review projection returned by generation.
type DreamReviewer ¶
type DreamReviewer interface {
Generate(context.Context, DreamTarget) (DreamReview, error)
Decide(context.Context, string, DreamDecision) (DreamReceipt, error)
}
DreamReviewer owns process-local plan retention and whole-plan decisions.
type DreamTarget ¶
type DreamTarget string
DreamTarget is a closed, transport-neutral manual-consolidation target.
const ( // DreamTargetProjectMemory selects the Build-owned project-memory store. DreamTargetProjectMemory DreamTarget = "project_memory" // DreamTargetUserModel selects the Build-owned cross-project user-model store. DreamTargetUserModel DreamTarget = "user_model" )
type DreamTargetCapability ¶
DreamTargetCapability describes one exact Build-owned target.
type ExplicitReflector ¶
ExplicitReflector submits one caller-owned completed session for reflection.
type FeatureScope ¶ added in v0.0.22
type FeatureScope struct {
// ClientMCPOnCreate reports whether this deployment accepts
// CreateSessionRequest.mcp_servers.
ClientMCPOnCreate bool
}
FeatureScope is what the DEPLOYMENT permits, as distinct from what the build implements. It is the "listener argument" serverFeatures' doc comment anticipated, in the shape ADR 0237 requires: a composition policy value, not an inference the server package makes from its own socket state.
One *Service backs both the gRPC and the HTTP listener, so this is decided ONCE at startup from the deployment's listener topology (mecated's clientMCPOnCreateForListeners) and handed in. A per-connection answer would be a different design needing its own ADR.
type ForkSuccessorRequest ¶ added in v0.0.24
type ForkSuccessorRequest struct {
Source session.SessionID
Placement SuccessorPlacement
Title string
ProviderID string
ModelID string
ReasoningEffort string
}
ForkSuccessorRequest is the additive internal successor shape used before the public contract cutover.
type HTTPHandler ¶
type HTTPHandler struct {
// contains filtered or unexported fields
}
HTTPHandler is the HTTP/SSE adapter over the shared Service. It serves the thin REST surface from ARCHITECTURE §7.2:
POST /v1/sessions -> CreateSession (JSON)
GET /v1/sessions/{id} -> GetSession (JSON snapshot)
DELETE /v1/sessions/{id} -> CloseSession (release session resources; 204)
POST /v1/sessions/{id}/rename -> RenameSession (persist an explicit title)
POST /v1/sessions/{id}/delete -> DeleteSession (physical snapshot + sidecars)
POST /v1/sessions/{id}/compact -> CompactSession (bodyless manual compaction)
POST /v1/sessions/{id}/prompt -> start a run; text/event-stream of Events
POST /v1/sessions/{id}/approve -> resolve the paused ask on the run
POST /v1/sessions/{id}/cancel -> cancel the in-flight run
POST /v1/sessions/{id}/cancel-child -> cancel ONE child (subagent) of the run
POST /v1/sessions/{id}/fork -> ForkSession (peer session from a history snapshot; 201)
GET /v1/sessions/{id}/events -> replay the durable event log; the stream ENDS
GET /v1/sessions/{id}/watch -> durable replay-then-follow; the stream STAYS OPEN
Every Event is emitted as one SSE `data:` line carrying the proto Event marshalled to JSON, so the HTTP and gRPC surfaces share one event shape.
func NewHTTPHandler ¶
func NewHTTPHandler(svc *Service) *HTTPHandler
NewHTTPHandler constructs an HTTPHandler over svc. The returned value is an http.Handler ready to mount.
func (*HTTPHandler) ServeHTTP ¶
func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP routes to the registered handlers.
type HarnessServer ¶
type HarnessServer struct {
mecatlv1.UnimplementedHarnessServiceServer
// contains filtered or unexported fields
}
HarnessServer implements the generated mecatlv1.HarnessServiceServer over the shared Service. It is the primary (gRPC) surface; the HTTP/SSE adapter wraps the same Service.
func NewHarnessServer ¶
func NewHarnessServer(svc *Service) *HarnessServer
NewHarnessServer constructs a HarnessServer over svc.
func (*HarnessServer) AbandonLearningAttempt ¶ added in v0.0.24
func (h *HarnessServer) AbandonLearningAttempt(ctx context.Context, req *mecatlv1.MutateLearningAttemptRequest) (*mecatlv1.MutateLearningAttemptResponse, error)
AbandonLearningAttempt abandons only the attempt; it performs no compensation.
func (*HarnessServer) ActivateLearnedSkill ¶
func (h *HarnessServer) ActivateLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*HarnessServer) ApplySessionCleanup ¶
func (h *HarnessServer) ApplySessionCleanup(ctx context.Context, req *mecatlv1.ApplySessionCleanupRequest) (*mecatlv1.CleanupJob, error)
func (*HarnessServer) ApplySessionMigration ¶
func (h *HarnessServer) ApplySessionMigration(ctx context.Context, req *mecatlv1.ApplySessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
func (*HarnessServer) ApprovePlan ¶
func (h *HarnessServer) ApprovePlan(req *mecatlv1.ApprovePlanRequest, stream mecatlv1.HarnessService_ApprovePlanServer) error
ApprovePlan atomically resolves a parked plan-approval ask and — on an allow verdict — streams the resumed run's AND the continuation run's events on one response stream. See Service.ApprovePlan for the contract.
func (*HarnessServer) ArchiveLearnedSkill ¶
func (h *HarnessServer) ArchiveLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*HarnessServer) CancelSessionCleanup ¶
func (h *HarnessServer) CancelSessionCleanup(ctx context.Context, req *mecatlv1.CancelSessionCleanupRequest) (*mecatlv1.CleanupJob, error)
func (*HarnessServer) CancelSessionMigration ¶
func (h *HarnessServer) CancelSessionMigration(ctx context.Context, req *mecatlv1.CancelSessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
func (*HarnessServer) CancelTeammate ¶
func (h *HarnessServer) CancelTeammate(ctx context.Context, req *mecatlv1.CancelTeammateRequest) (*mecatlv1.CancelTeammateResponse, error)
CancelTeammate cancels one member of a running team (issue #29).
func (*HarnessServer) CleanupTeam ¶
func (h *HarnessServer) CleanupTeam(ctx context.Context, req *mecatlv1.CleanupTeamRequest) (*mecatlv1.CleanupTeamResponse, error)
CleanupTeam tears down a finished team.
func (*HarnessServer) ClearSession ¶ added in v0.0.24
func (h *HarnessServer) ClearSession(ctx context.Context, req *mecatlv1.ClearSessionRequest) (*mecatlv1.ClearSessionResponse, error)
ClearSession creates a distinct empty-history successor.
func (*HarnessServer) CloseSession ¶
func (h *HarnessServer) CloseSession(ctx context.Context, req *mecatlv1.CloseSessionRequest) (*mecatlv1.CloseSessionResponse, error)
CloseSession ends a session and releases its server-side resources. It returns NotFound only for a never-created id; an already-released session succeeds (idempotent). It calls Service.EndSession, NOT the void Service.CloseSession, so an unknown id surfaces as NotFound rather than a silent success.
func (*HarnessServer) CompactSession ¶ added in v0.0.22
func (h *HarnessServer) CompactSession(ctx context.Context, req *mecatlv1.CompactSessionRequest) (*mecatlv1.CompactSessionResponse, error)
CompactSession applies one out-of-band compaction pass to an owned session.
func (*HarnessServer) Converse ¶
func (h *HarnessServer) Converse(stream mecatlv1.HarnessService_ConverseServer) error
Converse drives one run over a bidi stream. The first frame MUST be a Prompt or RetryStart; the server then relays the run's Events while concurrently reading ResumeApproval / Cancel control frames, until the events channel closes (the terminal result was delivered) or the stream context is cancelled.
func (*HarnessServer) CreateSession ¶
func (h *HarnessServer) CreateSession(ctx context.Context, req *mecatlv1.CreateSessionRequest) (*mecatlv1.CreateSessionResponse, error)
CreateSession allocates a new session and returns its id.
func (*HarnessServer) CreateTeam ¶
func (h *HarnessServer) CreateTeam(ctx context.Context, req *mecatlv1.CreateTeamRequest) (*mecatlv1.CreateTeamResponse, error)
CreateTeam allocates a new agent team and returns its id, enrolling the optional initial roster atomically (any member failure abandons the whole team). The enrolled roster is echoed back so the caller need not follow up with ListTeam.
func (*HarnessServer) DecideDreamPlan ¶
func (h *HarnessServer) DecideDreamPlan(ctx context.Context, req *mecatlv1.DecideDreamPlanRequest) (*mecatlv1.DecideDreamPlanResponse, error)
DecideDreamPlan applies or dismisses the exact retained review plan.
func (*HarnessServer) DecideLearningProposal ¶
func (h *HarnessServer) DecideLearningProposal(ctx context.Context, req *mecatlv1.DecideLearningProposalRequest) (*mecatlv1.DecideLearningProposalResponse, error)
DecideLearningProposal applies a version-checked approve or reject decision.
func (*HarnessServer) DeleteSession ¶
func (h *HarnessServer) DeleteSession(ctx context.Context, req *mecatlv1.DeleteSessionRequest) (*mecatlv1.DeleteSessionResponse, error)
DeleteSession physically removes an idle main session and store-managed sidecars.
func (*HarnessServer) DiffLearnedSkillVersions ¶
func (h *HarnessServer) DiffLearnedSkillVersions(ctx context.Context, req *mecatlv1.DiffLearnedSkillVersionsRequest) (*mecatlv1.DiffLearnedSkillVersionsResponse, error)
func (*HarnessServer) ForkSession ¶
func (h *HarnessServer) ForkSession(ctx context.Context, req *mecatlv1.ForkSessionRequest) (*mecatlv1.ForkSessionResponse, error)
ForkSession creates a history-carrying successor.
func (*HarnessServer) GenerateDreamPlan ¶
func (h *HarnessServer) GenerateDreamPlan(ctx context.Context, req *mecatlv1.GenerateDreamPlanRequest) (*mecatlv1.GenerateDreamPlanResponse, error)
GenerateDreamPlan creates a retained manual consolidation review.
func (*HarnessServer) GetCompatibilityInfo ¶ added in v0.0.22
func (h *HarnessServer) GetCompatibilityInfo(ctx context.Context, _ *mecatlv1.GetCompatibilityInfoRequest) (*mecatlv1.GetCompatibilityInfoResponse, error)
GetCompatibilityInfo returns the deployment's compatibility descriptor (ADR 0248).
Distinct from GetServerInfo above, which answers "which BUILD is this?" under ADR 0245's privacy boundary. This answers "what may I do with this server?" and carries exactly the capabilities/configuration that boundary keeps out of the identity response.
It is authenticated like every other RPC, which keeps UNAUTHENTICATED and UNIMPLEMENTED distinguishable at the client: the SDK treats UNIMPLEMENTED as "below the compatibility floor" and fails loudly, so an auth failure must not be able to masquerade as one.
func (*HarnessServer) GetLearnedSkill ¶
func (h *HarnessServer) GetLearnedSkill(ctx context.Context, req *mecatlv1.GetLearnedSkillRequest) (*mecatlv1.GetLearnedSkillResponse, error)
func (*HarnessServer) GetLearningAttempt ¶ added in v0.0.24
func (h *HarnessServer) GetLearningAttempt(ctx context.Context, req *mecatlv1.GetLearningAttemptRequest) (*mecatlv1.GetLearningAttemptResponse, error)
GetLearningAttempt returns one content-free attempt projection from the caller partition.
func (*HarnessServer) GetLearningProposal ¶
func (h *HarnessServer) GetLearningProposal(ctx context.Context, req *mecatlv1.GetLearningProposalRequest) (*mecatlv1.GetLearningProposalResponse, error)
GetLearningProposal returns bounded detail for one caller-owned proposal.
func (*HarnessServer) GetMcpPrompt ¶
func (h *HarnessServer) GetMcpPrompt(ctx context.Context, req *mecatlv1.GetMcpPromptRequest) (*mecatlv1.GetMcpPromptResponse, error)
GetMcpPrompt expands a named prompt with arguments on the named server.
func (*HarnessServer) GetServerInfo ¶ added in v0.0.22
func (h *HarnessServer) GetServerInfo(_ context.Context, req *mecatlv1.GetServerInfoRequest) (*mecatlv1.GetServerInfoResponse, error)
GetServerInfo returns safe build, composition, and the caller-selected provider endpoint projection.
func (*HarnessServer) GetSession ¶
func (h *HarnessServer) GetSession(ctx context.Context, req *mecatlv1.GetSessionRequest) (*mecatlv1.GetSessionResponse, error)
GetSession returns a snapshot of the requested session.
func (*HarnessServer) GetSessionCleanupJob ¶
func (h *HarnessServer) GetSessionCleanupJob(ctx context.Context, req *mecatlv1.GetSessionCleanupJobRequest) (*mecatlv1.CleanupJob, error)
func (*HarnessServer) GetSessionMigrationJob ¶
func (h *HarnessServer) GetSessionMigrationJob(ctx context.Context, req *mecatlv1.GetSessionMigrationJobRequest) (*mecatlv1.SessionMigrationJob, error)
func (*HarnessServer) GetSessionTranscript ¶
func (h *HarnessServer) GetSessionTranscript(ctx context.Context, req *mecatlv1.GetSessionTranscriptRequest) (*mecatlv1.GetSessionTranscriptResponse, error)
GetSessionTranscript returns the owned session's snapshot-derived transcript.
func (*HarnessServer) GetSoul ¶
func (h *HarnessServer) GetSoul(ctx context.Context, _ *mecatlv1.GetSoulRequest) (*mecatlv1.GetSoulResponse, error)
GetSoul returns the resolved soul (persona) snapshot.
func (*HarnessServer) GetStorageHealth ¶
func (h *HarnessServer) GetStorageHealth(ctx context.Context, _ *mecatlv1.GetStorageHealthRequest) (*mecatlv1.GetStorageHealthResponse, error)
GetStorageHealth returns authenticated aggregate storage status.
func (*HarnessServer) GetUserModel ¶
func (h *HarnessServer) GetUserModel(ctx context.Context, req *mecatlv1.GetUserModelRequest) (*mecatlv1.GetUserModelResponse, error)
GetUserModel returns the current user-model index snapshot.
func (*HarnessServer) ListAgents ¶
func (h *HarnessServer) ListAgents(ctx context.Context, _ *mecatlv1.ListAgentsRequest) (*mecatlv1.ListAgentsResponse, error)
ListAgents returns the resolved agent-definition inventory snapshot.
func (*HarnessServer) ListCommands ¶
func (h *HarnessServer) ListCommands(ctx context.Context, req *mecatlv1.ListCommandsRequest) (*mecatlv1.ListCommandsResponse, error)
ListCommands returns commands for an owned session placement.
func (*HarnessServer) ListLearnedSkills ¶
func (h *HarnessServer) ListLearnedSkills(ctx context.Context, req *mecatlv1.ListLearnedSkillsRequest) (*mecatlv1.ListLearnedSkillsResponse, error)
func (*HarnessServer) ListLearningAttempts ¶ added in v0.0.24
func (h *HarnessServer) ListLearningAttempts(ctx context.Context, req *mecatlv1.ListLearningAttemptsRequest) (*mecatlv1.ListLearningAttemptsResponse, error)
ListLearningAttempts returns one bounded attempt page from the caller partition.
func (*HarnessServer) ListLearningProposals ¶
func (h *HarnessServer) ListLearningProposals(ctx context.Context, req *mecatlv1.ListLearningProposalsRequest) (*mecatlv1.ListLearningProposalsResponse, error)
ListLearningProposals returns one bounded proposal page for the caller partition.
func (*HarnessServer) ListMcpPrompts ¶
func (h *HarnessServer) ListMcpPrompts(ctx context.Context, req *mecatlv1.ListMcpPromptsRequest) (*mecatlv1.ListMcpPromptsResponse, error)
ListMcpPrompts returns the prompt snapshots for the requested server (empty server = all). Nil provider yields an empty list.
func (*HarnessServer) ListMcpResources ¶
func (h *HarnessServer) ListMcpResources(ctx context.Context, req *mecatlv1.ListMcpResourcesRequest) (*mecatlv1.ListMcpResourcesResponse, error)
ListMcpResources returns the resource snapshots for the requested server (empty server = all). Nil provider yields an empty list.
func (*HarnessServer) ListMcpSources ¶
func (h *HarnessServer) ListMcpSources(ctx context.Context, _ *mecatlv1.ListMcpSourcesRequest) (*mecatlv1.ListMcpSourcesResponse, error)
ListMcpSources returns the resolved MCP source inventory snapshot.
func (*HarnessServer) ListModels ¶
func (h *HarnessServer) ListModels(ctx context.Context, _ *mecatlv1.ListModelsRequest) (*mecatlv1.ListModelsResponse, error)
ListModels returns the resolved selectable-model inventory snapshot plus (issue #262) the per-provider live-listing status. ListModels itself triggers the on-demand refresh (when installed), so ProviderStatuses is read AFTER it to reflect the just-completed refresh.
func (*HarnessServer) ListSessions ¶
func (h *HarnessServer) ListSessions(ctx context.Context, req *mecatlv1.ListSessionsRequest) (*mecatlv1.ListSessionsResponse, error)
ListSessions returns the stored-session inventory — the picker metadata a client renders to let an operator open an EXISTING session by id (issue #245 Phase 1).
func (*HarnessServer) ListSkillChanges ¶
func (h *HarnessServer) ListSkillChanges(ctx context.Context, req *mecatlv1.ListSkillChangesRequest) (*mecatlv1.ListSkillChangesResponse, error)
func (*HarnessServer) ListSkills ¶
func (h *HarnessServer) ListSkills(ctx context.Context, _ *mecatlv1.ListSkillsRequest) (*mecatlv1.ListSkillsResponse, error)
ListSkills returns the resolved skills-inventory snapshot.
func (*HarnessServer) ListTeam ¶
func (h *HarnessServer) ListTeam(ctx context.Context, req *mecatlv1.ListTeamRequest) (*mecatlv1.ListTeamResponse, error)
ListTeam returns a snapshot of the team roster, task list, and completion state.
func (*HarnessServer) ListToolHiveGroups ¶
func (h *HarnessServer) ListToolHiveGroups(ctx context.Context, _ *mecatlv1.ListToolHiveGroupsRequest) (*mecatlv1.ListToolHiveGroupsResponse, error)
ListToolHiveGroups returns the distinct, non-empty ToolHive groups derived from the inventory snapshot.
func (*HarnessServer) ListWorktrees ¶
func (h *HarnessServer) ListWorktrees(ctx context.Context, req *mecatlv1.ListWorktreesRequest) (*mecatlv1.ListWorktreesResponse, error)
ListWorktrees returns scoped placement choices for an owned session.
func (*HarnessServer) PlanSessionCleanup ¶
func (h *HarnessServer) PlanSessionCleanup(ctx context.Context, req *mecatlv1.PlanSessionCleanupRequest) (*mecatlv1.PlanSessionCleanupResponse, error)
PlanSessionCleanup returns a caller-bound read-only retention plan.
func (*HarnessServer) PlanSessionMigration ¶
func (h *HarnessServer) PlanSessionMigration(ctx context.Context, _ *mecatlv1.PlanSessionMigrationRequest) (*mecatlv1.SessionMigrationPlan, error)
func (*HarnessServer) ReadMcpResource ¶
func (h *HarnessServer) ReadMcpResource(ctx context.Context, req *mecatlv1.ReadMcpResourceRequest) (*mecatlv1.ReadMcpResourceResponse, error)
ReadMcpResource reads a single resource by URI from the named server.
func (*HarnessServer) ReflectSession ¶
func (h *HarnessServer) ReflectSession(ctx context.Context, req *mecatlv1.ReflectSessionRequest) (*mecatlv1.ReflectSessionResponse, error)
ReflectSession submits one caller-owned completed session to the bounded coordinator.
func (*HarnessServer) RejectLearnedSkill ¶
func (h *HarnessServer) RejectLearnedSkill(ctx context.Context, req *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*HarnessServer) RenameSession ¶
func (h *HarnessServer) RenameSession(ctx context.Context, req *mecatlv1.RenameSessionRequest) (*mecatlv1.RenameSessionResponse, error)
RenameSession explicitly replaces an idle main session's persisted title.
func (*HarnessServer) ResumeSessionMigration ¶
func (h *HarnessServer) ResumeSessionMigration(ctx context.Context, req *mecatlv1.ResumeSessionMigrationRequest) (*mecatlv1.SessionMigrationJob, error)
func (*HarnessServer) RetryLearningAttempt ¶ added in v0.0.24
func (h *HarnessServer) RetryLearningAttempt(ctx context.Context, req *mecatlv1.MutateLearningAttemptRequest) (*mecatlv1.MutateLearningAttemptResponse, error)
RetryLearningAttempt retries one failed attempt under opaque-version CAS.
func (*HarnessServer) RollbackLearnedSkill ¶
func (h *HarnessServer) RollbackLearnedSkill(ctx context.Context, req *mecatlv1.RollbackLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*HarnessServer) RunTeam ¶
func (h *HarnessServer) RunTeam(req *mecatlv1.RunTeamRequest, stream mecatlv1.HarnessService_RunTeamServer) error
RunTeam drives the team to quiescence, streaming every member event tagged with the producing member, then ends the stream with the single terminal frame carrying TeamEvent.outcome (issue #36). Supervisor.Run serialises sink calls through a single forwarder, so stream.Send is never invoked concurrently. A send failure cancels the run so the team stops promptly rather than running on to quiescence unseen; the outcome frame is sent only when no send has failed (relay-drain discipline — a dead client gets no further writes).
func (*HarnessServer) SendTeammateMessage ¶
func (h *HarnessServer) SendTeammateMessage(ctx context.Context, req *mecatlv1.SendTeammateMessageRequest) (*mecatlv1.SendTeammateMessageResponse, error)
SendTeammateMessage posts a message into a member's inbox.
func (*HarnessServer) SetMode ¶
func (h *HarnessServer) SetMode(ctx context.Context, req *mecatlv1.SetModeRequest) (*mecatlv1.SetModeResponse, error)
SetMode changes the permission posture of the requested session.
func (*HarnessServer) SpawnTeammate ¶
func (h *HarnessServer) SpawnTeammate(ctx context.Context, req *mecatlv1.SpawnTeammateRequest) (*mecatlv1.SpawnTeammateResponse, error)
SpawnTeammate enrols a member in a team and returns its roster entry.
func (*HarnessServer) StreamSessionEvents ¶
func (h *HarnessServer) StreamSessionEvents(req *mecatlv1.StreamSessionEventsRequest, stream grpc.ServerStreamingServer[mecatlv1.Event]) error
StreamSessionEvents replays a session's durable event log as a server stream of Event envelopes (issue #245 Phase 1; cloud-native Phase 3a read-back).
func (*HarnessServer) StreamSessionLive ¶
func (h *HarnessServer) StreamSessionLive(req *mecatlv1.StreamSessionLiveRequest, stream grpc.ServerStreamingServer[mecatlv1.Event]) error
StreamSessionLive is the LIVE per-session event stream (ADR 0075 fire-result-delivery Scenario 6 / Wave 3): a thin transport over the in-process per-session subscription registry (Service.Subscribe / PublishSessionEvent). It is the UNIFIED bridge serving BOTH the embedded mecatui (which dials its in-process server over a real gRPC UNIX socket) AND a remote mecated — ONE proto, ONE TUI consumption path.
Relay discipline: the live wire applies the SAME log-only skip as the live Converse relay, with ONE narrow exception — a fire-result DELIVERY note (an EvUserPrompt whose text starts with the renderFireDelivery provenance header "[scheduled task ") is RELAYED so a connected client renders the delivery card as it happens (AC6.2). The other two log-only kinds (EvApproval, EvCompactionArchive) stay SKIPPED, and a non-delivery EvUserPrompt stays skipped too (the client already holds its own prompt). The delivery note is metadata-only/redacted by construction (gauntlet #7).
Drain-to-discard: a Send error (dead/disconnected client) cancels the subscription and the handler returns — the in-process registry's PublishSessionEvent is already non-blocking (a full subscriber channel drops the event, AC6.3), so a dead client never wedges the delivery run. The durable log records the tail regardless (it is appended by the relay/loop, independent of this stream).
func (*HarnessServer) UndoLearningPromotion ¶
func (h *HarnessServer) UndoLearningPromotion(ctx context.Context, req *mecatlv1.UndoLearningPromotionRequest) (*mecatlv1.UndoLearningPromotionResponse, error)
UndoLearningPromotion applies a version-checked compensating promotion undo.
func (*HarnessServer) WatchSessionEvents ¶ added in v0.0.22
func (h *HarnessServer) WatchSessionEvents(req *mecatlv1.WatchSessionEventsRequest, stream grpc.ServerStreamingServer[mecatlv1.WatchSessionEventsResponse]) error
WatchSessionEvents is the DURABLE replay-then-follow stream (issue #821, ADR 0250): a thin transport over Service.WatchSessionEvents.
The SSE route GET /v1/sessions/{id}/watch consumes the SAME service method, so the two transports deliver identical envelope sequences by construction rather than by parallel maintenance (AC7.3). Everything below is framing.
Relay discipline mirrors StreamSessionEvents, NOT the live wire: this is the READ-BACK of the durable log, so ALL events are relayed including the three log-only kinds (EvApproval/EvCompactionArchive/EvUserPrompt) — a client replaying a session wants the verdicts and prompts, as they ARE the transcript. Do NOT copy relayLiveEvent's filter here.
A Send error means the client is gone: break out of the iterator, which releases the watch and its backend read per port.CursorEventLog's contract. There is no drain-to-discard to do — unlike a live relay, this stream pulls from durable storage and has no run whose emits could wedge behind it.
type HealthHandler ¶
type HealthHandler struct {
// contains filtered or unexported fields
}
HealthHandler serves the liveness and readiness probes. Liveness (/healthz) always returns 200 while the process is up; readiness (/readyz) returns 200 only when the injected ReadyFunc reports ready, else 503. These endpoints carry no secrets and MUST be mounted outside the auth/rate-limit middleware so orchestrators can probe them without credentials.
func NewHealthHandler ¶
func NewHealthHandler(ready ReadyFunc) *HealthHandler
NewHealthHandler constructs a HealthHandler. A nil ready func means the server is considered ready as soon as the process is up.
func (*HealthHandler) Live ¶
func (*HealthHandler) Live(w http.ResponseWriter, _ *http.Request)
Live handles GET /healthz: a pure liveness probe that is 200 whenever the process can serve HTTP at all.
func (*HealthHandler) Ready ¶
func (h *HealthHandler) Ready(w http.ResponseWriter, _ *http.Request)
Ready handles GET /readyz: 200 when dependencies are ready, 503 otherwise.
func (*HealthHandler) RegisterHealth ¶
func (h *HealthHandler) RegisterHealth(mux *http.ServeMux)
RegisterHealth mounts the health endpoints on mux. They are intentionally registered on the outer mux (before auth) so they bypass authentication and rate limiting.
type IDGenerator ¶
IDGenerator returns a fresh, unique session id. It defaults to a random hex id when nil; tests may inject a deterministic generator.
type ListSessionsPage ¶
type ListSessionsPage struct {
Sessions []SessionSummary
NextCursor string
TotalCount int
}
ListSessionsPage is one bounded owner-filtered inventory response.
type ListSessionsPageRequest ¶
ListSessionsPageRequest asks for one bounded inventory page.
type MemberEngineFactory ¶
type MemberEngineFactory func(t *team.Team, spec agent.MemberSpec, routedModel string) agent.MemberBuild
MemberEngineFactory builds a team member's engine (and its optional per-member permission mode), binding it to the shared team (so the member's catalog includes that team's coordination tools) and shaping it from the member spec (read-only base vs mutating tools, the member's agent definition). The composition root supplies it via Config.MemberEngine; CreateTeam adapts it to an agent.MemberEngine by capturing the per-team aggregate. Returning agent.MemberBuild (rather than a bare *Engine) is how a def's permissionMode reaches the supervisor's per-member session.
routedModel is the OPT-IN model router's classification (ADR 0034) — the ALREADY-RESOLVED concrete model id for an UNDEFINED member, "" otherwise (no router, a miss, or a DEFINED member whose def pins its own model). It is the same shape as agent.TeamMemberEngineFactory so one factory satisfies both. On the gRPC RunTeam path the supervisor runs ZERO-CAPS (no parent caps ⇒ no routeTask), so routedModel is always "" there and the member is built byte-identically to today.
type MigrationItemError ¶
MigrationItemError is a bounded stable maintenance failure. ItemHandle is a non-reversible display handle, never a session id or backend path.
type MigrationJob ¶
type MigrationJob struct {
ID string
State string
V1Families int64
V2Families int64
InvalidFamilies int64
SkippedFamilies int64
CurrentBytes int64
ReclaimableBytes int64
TemporaryBytes int64
Processed int64
Migrated int64
Failed int64
Errors []MigrationItemError
}
MigrationJob is the caller-safe durable job projection.
type MigrationPlan ¶
type MigrationPlan struct {
ID string
Available bool
V1Families int64
V2Families int64
InvalidFamilies int64
SkippedFamilies int64
CurrentBytes int64
ReclaimableBytes int64
TemporaryBytes int64
}
MigrationPlan is the content-free, authenticated projection of a read-only v1-to-v2 storage inspection.
type OwnerlessCutoverInventory ¶
type OwnerlessCutoverInventory struct {
SessionsAvailable bool
SessionCount int
SessionIDs []string
SessionIDsTruncated bool
SchedulesAvailable bool
ScheduleCount int
ScheduleNames []string
ScheduleNamesTruncated bool
}
OwnerlessCutoverInventory is a content-free preflight for enabling caller ownership. Identifiers are bounded samples; counts cover the full available inventory. Availability is per family because custom stores may expose only one metadata seam.
type PlacementBindRequest ¶ added in v0.0.24
type PlacementBindRequest struct {
Selector PlacementSelector
Principal *session.Principal
Scope PlacementScope
Operation PlacementOperation
}
PlacementBindRequest contains the complete authorization context a provider needs to atomically authorize and resolve one placement record version.
type PlacementBinder ¶ added in v0.0.24
type PlacementBinder struct {
// contains filtered or unexported fields
}
PlacementBinder is the server-owned choke point around one deployment provider. It exposes no registry, inventory, signer, cache, or split authorization/resolution operation.
func NewPlacementBinder ¶ added in v0.0.24
func NewPlacementBinder(provider PlacementProvider) (*PlacementBinder, error)
NewPlacementBinder constructs the binding choke point.
func (*PlacementBinder) Bind ¶ added in v0.0.24
func (b *PlacementBinder) Bind(ctx context.Context, req PlacementBindRequest) (PlacementBinding, error)
Bind validates the request, delegates exactly one atomic operation to the provider, then validates that the returned environment and exact ref agree.
func (*PlacementBinder) Reattach ¶ added in v0.0.24
func (b *PlacementBinder) Reattach(ctx context.Context, req PlacementReattachRequest) (PlacementBinding, error)
Reattach resolves only the exact persisted ref. Providers without the explicit reattachment capability fail closed; Bind is never used as fallback.
type PlacementBinding ¶ added in v0.0.24
type PlacementBinding struct {
Environment tool.Environment
Ref session.EnvironmentRef
Metadata PlacementMetadata
// Close releases provisional provider resources. It is called after creation
// because ordinary bindings are reattached fresh at run entry.
Close func() error
}
PlacementBinding is the indivisible successful result of Bind.
type PlacementDiscoverer ¶ added in v0.0.24
type PlacementDiscoverer interface {
ListWorktrees(context.Context, PlacementDiscoveryRequest) ([]ScopedWorktree, error)
}
PlacementDiscoverer is the provider-owned discovery capability. Worktree availability is derived only from the mandatory PlacementProvider implementing this interface; there is no independent Config discovery seam.
type PlacementDiscoveryRequest ¶ added in v0.0.24
type PlacementDiscoveryRequest struct {
Source session.SessionID
SourceRef session.EnvironmentRef
Principal *session.Principal
Scope PlacementScope
}
PlacementDiscoveryRequest scopes alternate-worktree discovery to an owned source and its exact current placement.
type PlacementMetadata ¶ added in v0.0.24
PlacementMetadata is the bounded, display-safe provider projection returned with a binding. It contains no roots, locators, credentials, or authority.
type PlacementOperation ¶ added in v0.0.24
type PlacementOperation string
PlacementOperation identifies the server operation requesting a new binding.
const ( // PlacementOperationCreate binds a placement for a new root session. PlacementOperationCreate PlacementOperation = "create" // PlacementOperationSuccessor binds or reauthorizes a placement while // creating a successor session. PlacementOperationSuccessor PlacementOperation = "successor" )
type PlacementProvider ¶ added in v0.0.24
type PlacementProvider interface {
Bind(context.Context, PlacementBindRequest) (PlacementBinding, error)
}
PlacementProvider owns placement authorization, atomic binding resolution, and private environment construction.
type PlacementReattachRequest ¶ added in v0.0.24
type PlacementReattachRequest struct {
Ref session.EnvironmentRef
Principal *session.Principal
Scope PlacementScope
}
PlacementReattachRequest carries the trusted authorization context and the exact durable identity to reattach. Ref must include Kind, ID, and Revision.
type PlacementReattacher ¶ added in v0.0.24
type PlacementReattacher interface {
Reattach(context.Context, PlacementReattachRequest) (PlacementBinding, error)
}
PlacementReattacher is the exact persisted-ref half of a placement provider. It is separate from PlacementProvider so legacy providers cannot accidentally receive a reattachment request through Bind and follow their current default.
type PlacementScope ¶ added in v0.0.24
type PlacementScope string
PlacementScope is a trusted composition-owned authorization scope. It is not derived from a placement ID or filesystem path.
type PlacementSelector ¶ added in v0.0.24
type PlacementSelector struct {
Kind PlacementSelectorKind
ID string
Source session.SessionID
SourceRef session.EnvironmentRef
}
PlacementSelector is the private server/provider binding protocol. It never crosses the engine or public transport boundary.
func DefaultPlacement ¶ added in v0.0.24
func DefaultPlacement() PlacementSelector
DefaultPlacement selects the provider-owned deployment default.
func NoFSPlacement ¶ added in v0.0.24
func NoFSPlacement() PlacementSelector
NoFSPlacement selects explicit filesystem attenuation.
func SelectWorktree ¶ added in v0.0.24
func SelectWorktree(source session.SessionID, ref session.EnvironmentRef, token string) PlacementSelector
SelectWorktree carries a source-scoped ephemeral selector to the provider.
func (PlacementSelector) IsDefault ¶ added in v0.0.24
func (s PlacementSelector) IsDefault() bool
IsDefault reports whether the deployment default was selected.
func (PlacementSelector) IsNoFS ¶ added in v0.0.24
func (s PlacementSelector) IsNoFS() bool
IsNoFS reports whether filesystem attenuation was selected.
func (PlacementSelector) IsWorktree ¶ added in v0.0.24
func (s PlacementSelector) IsWorktree() bool
IsWorktree reports whether a source-scoped worktree token was selected.
func (PlacementSelector) Valid ¶ added in v0.0.24
func (s PlacementSelector) Valid() bool
Valid reports whether the selector has exactly one valid protocol shape.
type PlacementSelectorKind ¶ added in v0.0.24
type PlacementSelectorKind string
PlacementSelectorKind is the closed private binding vocabulary.
const ( PlacementSelectorDefault PlacementSelectorKind = "default" PlacementSelectorNoFS PlacementSelectorKind = "no-fs" PlacementSelectorWorktree PlacementSelectorKind = "worktree" )
Private placement selector kinds.
type PrincipalValidator ¶
type PrincipalValidator interface {
Validate(ctx context.Context, bearer string) (*session.Principal, error)
}
PrincipalValidator verifies a bearer credential and returns the caller it vouches for (ADR 0204 decision 3). Validation itself — JWT parse, signature, alg, issuer/audience/exp/nbf, JWKS fetch and rotation — is DELEGATED to the implementation (toolhive-core/authn); mecatl hand-rolls none of it. This narrow interface is the seam: the edge only decides what to do with the verdict.
A nil returned principal with a nil error is treated as a rejection: the edge never fabricates an anonymous caller (absent identity is a nil principal, and that only happens when NO validator is wired at all).
type ProposalPromoter ¶
type ProposalPromoter func(context.Context, learning.ProposalPartition, learning.ProposalID, learning.ProposalVersion, bool) (learning.ProposalRecord, error)
ProposalPromoter applies an approved proposal through the configured memory target.
type ProposalUndoer ¶
type ProposalUndoer func(context.Context, learning.ProposalPartition, learning.ProposalID, learning.ProposalVersion) (learning.ProposalRecord, error)
ProposalUndoer commits a compensating memory revision for a promoted proposal.
type ProviderSelector ¶
type ProviderSelector struct {
// ProviderID is the registry id ("" => server default).
ProviderID string
// ModelID is the model selector ("" => provider default; a non-empty id the
// catalog doesn't know is passed through to the provider verbatim).
ModelID string
// ReasoningEffort is the per-session reasoning-effort selector (ADR 0055): a
// NEUTRAL token ("" / "auto" => unset, the operator default applies; otherwise
// low/medium/high/xhigh/max). It is OPAQUE to the adapter — the composition root
// normalises + clamps it per provider and re-mints the engine's adapter when it
// differs from the operator default. "" keeps the operator default (and the
// shared engine on a byte-identical default path). It composes orthogonally with
// ProviderID/ModelID; unlike ModelID it is meaningful WITHOUT a ProviderID (it
// rides the server-default provider).
ReasoningEffort string
}
ProviderSelector names a per-session provider+model (multi-provider Phase 0, S3). The zero value (both empty) means "server default" — the shared engine, no per-session build. It is a NEUTRAL value object owned by the server adapter: the composition root (internal/app) resolves it against the registry/catalog; the adapter never imports either. Setting ModelID with an empty ProviderID is a client error (a bare model on an env-derived default provider is ambiguous) — rejected at the create boundary before the factory is consulted.
type ReadyFunc ¶
type ReadyFunc func() bool
ReadyFunc reports whether the server's dependencies (engine, store) are ready to serve traffic. It is consulted by the /readyz handler on every probe so readiness can change at runtime. A nil ReadyFunc is treated as always-ready.
type ReflectionReceipt ¶
type ReflectionReceipt struct {
ID string
Disposition string
Queued int
Abstained bool
Staged int
Promoted int
Conflicted int
}
ReflectionReceipt is the bounded result of an explicit reflection request.
type ResolvedModel ¶
type ResolvedModel struct {
ProviderID string
ModelID string
ContextWindow int64
// ReasoningEffort is the effective per-session reasoning-effort token (ADR
// 0055), "" when unset. Carried on the resolved-model echo so the wire (and the
// TUI footer) can show the active effort; it is the value held on the
// per-session engine record (sessionEngine.reasoningEffort), not recomputed.
ReasoningEffort string
}
ResolvedModel is the per-session EFFECTIVE model echoed on the wire: the provider+model id this session resolved to plus its context window. It is the SINGLE composition-computed value (see Config.DefaultResolvedModel and the per-session SessionEngineResult fields) — the server holds it and echoes it verbatim, mirroring the capability-intersection single-source rule; a handler must never read it back off the request (model_id is empty for a default session and ambiguous for passthrough).
type RetentionPolicy ¶
type RetentionPolicy struct {
// Version changes whenever the effective policy semantics change. When empty,
// the server derives a stable version from the scalar limits.
Version string
MainMaxAge time.Duration
MainMaxCount int
ChildMaxAge time.Duration
ChildMaxCount int
ScheduledMaxAge time.Duration
ScheduledMaxCount int
SweepCadence time.Duration
}
RetentionPolicy is the effective process-wide session retention policy. Zero values disable their respective limit or cadence.
type RunEventRecorder ¶ added in v0.0.22
type RunEventRecorder struct {
// contains filtered or unexported fields
}
RunEventRecorder is the run-scoped durable projection of a live event stream. It coalesces high-frequency text deltas while leaving the client-facing stream untouched. Close must be called after the stream has been drained.
func NewRunEventRecorder ¶ added in v0.0.22
NewRunEventRecorder creates a recorder for one relayed run or merged relay.
func (*RunEventRecorder) Close ¶ added in v0.0.22
func (r *RunEventRecorder) Close()
Close attempts any incomplete final turn's buffered deltas once.
func (*RunEventRecorder) Observe ¶ added in v0.0.22
func (r *RunEventRecorder) Observe(ev session.Event)
Observe adds ev to the durable projection. Delta events are buffered in bounded UTF-8 chunks; every other event first flushes buffered deltas and is then appended itself. Every projected event is attempted exactly once because EventLog.Append may return an error after durably writing it.
type ScheduleManagerConfig ¶
type ScheduleManagerConfig struct {
// Store is the port.SessionStore the schedule's origin validation reads
// (validateScheduleOrigin). It is also the ScheduleStore discovery source
// when ScheduleStore is nil (the scheduleStoreProvider accessor).
Store port.SessionStore
// ScheduleStore is the OPTIONAL explicit port.ScheduleStore override (the
// --schedule-store-url composition path). When non-nil it WINS over the
// Store type-assertion discovery: the registry is a remote driver, not the
// session store's own accessor, so the in-chat Schedule tool + the tick
// loop + the fire path all share the ONE resolveScheduleStore resolution.
// When nil, behaviour is byte-identical to the accessor discovery.
ScheduleStore port.ScheduleStore
Now func() time.Time
Models *atomic.Pointer[[]*mecatlv1.ModelInfo]
Diagnostics port.Diagnostics
// OwnershipEnforced mirrors server.Config.OwnershipEnforced (true only when
// the request edge has a verifier wired). It gates whether the manager
// namespaces the store-facing schedule key by verified caller (issue #368,
// ADR-0212 decision 1): a schedule Name is a caller-chosen, human-readable
// key exactly like a memory key, so two DIFFERENT owners may legitimately
// pick the identical name without colliding — mirroring
// memory.CallerStore's owner-digest scheme. When false (no verifier wired,
// or a standalone-constructed manager that leaves this unset), the key is
// BYTE-IDENTICAL to today: the bare literal name, one flat namespace.
OwnershipEnforced bool
}
ScheduleManagerConfig is the pre-Service construction input for a scheduleManager (ADR 0076): the plain inputs available before buildEngine. Store is a port.SessionStore; the ScheduleStore is type-asserted off it via the scheduleStoreProvider accessor (the jsonlstore + redisstore expose one). ScheduleStore is the OPTIONAL explicit override (the --schedule-store-url composition path): when non-nil it WINS over the accessor discovery, so a driver-backed registry backs the in-chat Schedule TOOL too — the registry is a remote driver, NOT the session store's own accessor, so the tool + tick loop + fire path share the ONE resolveScheduleStore resolution (no split-brain with an accessor-ful store + the override, and no absent tool with an accessor-less store + the override). When nil, behaviour is byte-identical to the accessor discovery (the pre-override posture). Now is the now-func (the same clock the Service uses). Models is the SHARED model-inventory pointer (selector validation reads *models.Load()); the Service passes its own pointer so SetModels keeps working with no second copy. Diagnostics is the operational diagnostic seam; nil-safe. A store that backs no ScheduleStore yields a nil manager (NewScheduleManager returns nil).
type ScheduleManagerImpl ¶
type ScheduleManagerImpl = scheduleManager
ScheduleManagerImpl exposes the concrete manager type to the COMPOSITION layer (internal/app) only: buildEngine must thread the ONE manager it constructs through to server.Config.ScheduleManager, and an interface-typed round-trip would re-introduce the typed-nil hazard (a nil *scheduleManager boxed in a non-nil port.ScheduleManager defeats the mgr == nil honest-absence gate). Consumers still interact with the manager via the port.ScheduleManager interface; the alias exists ONLY so the concrete value crosses the adapter boundary with its nil-ness intact. It adds no methods to the public surface beyond what port.ScheduleManager already declares.
type ScheduleServer ¶
type ScheduleServer struct {
mecatlv1.UnimplementedScheduleServiceServer
// contains filtered or unexported fields
}
ScheduleServer implements mecatlv1.ScheduleServiceServer over the shared Service. It embeds UnimplementedScheduleServiceServer for forward compatibility (require_unimplemented_servers=true in buf.gen.yaml), mirroring HarnessServer's embed of UnimplementedHarnessServiceServer.
func NewScheduleServer ¶
func NewScheduleServer(svc *Service) *ScheduleServer
NewScheduleServer constructs a ScheduleServer over svc.
func (*ScheduleServer) CreateSchedule ¶
func (h *ScheduleServer) CreateSchedule(ctx context.Context, req *mecatlv1.CreateScheduleRequest) (*mecatlv1.CreateScheduleResponse, error)
CreateSchedule saves a new schedule (upsert by name) and returns the created aggregate. The create-seam (composition) validates the spec fail-closed — the trigger XOR, the prompt-or-parts rule, the cron grammar, the Mutating/Mode invariant — so the handler only checks the wire-required fields (spec non-nil, name non-empty) and delegates.
func (*ScheduleServer) DeleteSchedule ¶
func (h *ScheduleServer) DeleteSchedule(ctx context.Context, req *mecatlv1.DeleteScheduleRequest) (*mecatlv1.DeleteScheduleResponse, error)
DeleteSchedule removes the schedule stored under name. Idempotent.
func (*ScheduleServer) FireNow ¶
func (h *ScheduleServer) FireNow(ctx context.Context, req *mecatlv1.FireNowRequest) (*mecatlv1.FireNowResponse, error)
FireNow forces an immediate fire of the schedule, returning the per-fire session id (fire_id == session_id on the wire).
func (*ScheduleServer) GetFire ¶
func (h *ScheduleServer) GetFire(ctx context.Context, req *mecatlv1.GetFireRequest) (*mecatlv1.GetFireResponse, error)
GetFire returns the fire record stored under fire_id.
func (*ScheduleServer) GetSchedule ¶
func (h *ScheduleServer) GetSchedule(ctx context.Context, req *mecatlv1.GetScheduleRequest) (*mecatlv1.GetScheduleResponse, error)
GetSchedule returns the schedule stored under name.
func (*ScheduleServer) ListFires ¶
func (h *ScheduleServer) ListFires(ctx context.Context, req *mecatlv1.ListFiresRequest) (*mecatlv1.ListFiresResponse, error)
ListFires returns the fire records for a schedule.
func (*ScheduleServer) ListSchedules ¶
func (h *ScheduleServer) ListSchedules(ctx context.Context, _ *mecatlv1.ListSchedulesRequest) (*mecatlv1.ListSchedulesResponse, error)
ListSchedules returns all stored schedules.
func (*ScheduleServer) PauseSchedule ¶
func (h *ScheduleServer) PauseSchedule(ctx context.Context, req *mecatlv1.PauseScheduleRequest) (*mecatlv1.PauseScheduleResponse, error)
PauseSchedule disables a schedule without deleting it (Enabled=false).
func (*ScheduleServer) ResumeSchedule ¶
func (h *ScheduleServer) ResumeSchedule(ctx context.Context, req *mecatlv1.ResumeScheduleRequest) (*mecatlv1.ResumeScheduleResponse, error)
ResumeSchedule re-enables a paused schedule (Enabled=true).
func (*ScheduleServer) UpdateSchedule ¶
func (h *ScheduleServer) UpdateSchedule(ctx context.Context, req *mecatlv1.UpdateScheduleRequest) (*mecatlv1.UpdateScheduleResponse, error)
UpdateSchedule updates an existing schedule's spec (the State half is preserved on overwrite — the Service.Load+Save discipline).
type ScopedWorktree ¶ added in v0.0.24
ScopedWorktree is the additive path-free discovery projection used by the server-owned placement cutover. Selector is ephemeral authority; the other fields are display-only.
type SecurityConfig ¶
type SecurityConfig struct {
// AuthToken, when non-empty, requires every RPC/request to present
// Authorization: Bearer <AuthToken> (gRPC: the "authorization" metadata
// header). An empty token disables authentication (dev mode).
AuthToken string
// RateLimit is the sustained per-client request rate in requests/second. A
// value <= 0 disables rate limiting entirely. Limiting is applied both
// per-client (keyed by token or peer IP) and globally.
RateLimit float64
// RateBurst is the token-bucket burst size. It defaults to a small multiple
// of RateLimit when left zero (see newLimiterSet).
RateBurst int
// Validator, when non-nil, turns caller identity ON: every request must
// present a bearer the validator vouches for, and the verified principal is
// stashed on the handler context (session.WithPrincipal). Nil (the default)
// leaves the path byte-identical to a mecatl without identity: no
// validation, no principal, no new failure mode.
Validator PrincipalValidator
// Diagnostics receives sanitized authentication-rejection records. Nil leaves
// diagnostics disabled; records never include credentials or validator errors.
Diagnostics port.Diagnostics
}
SecurityConfig configures the reusable authentication and rate-limiting interceptors/middleware shared by the gRPC and HTTP surfaces. The zero value is a valid, fully permissive (dev) configuration: no token is required and no rate limit is enforced.
Both knobs compose with the loopback-default + off-loopback warning in the composition root (cmd/mecated): an empty AuthToken on a non-loopback bind is a loud-but-not-fatal misconfiguration the operator is warned about.
type Service ¶
type Service struct {
// contains filtered or unexported fields
}
Service is the surface-agnostic application service shared by the gRPC and HTTP/SSE adapters. It owns session lifecycle (create/lookup), starts runs on the shared engine, and keeps a registry of in-flight runs keyed by session id so out-of-band approve/cancel reach the right run. It is safe for concurrent use.
Persistence and auto-resume ¶
The engine mutates the session in place over a run and the Service persists it to the SessionStore at meaningful transitions: at create, when the run pauses awaiting approval (see Persist), and at run end. With a durable store (jsonlstore via --store-dir) the latest snapshot therefore survives a process restart.
A registered run is removed by the wire adapter that owns the stream: each adapter `defer`s FinishRun(id, run) after it finishes draining run.Events() (the channel closes when the run terminates). The Service does not deregister runs on its own — there is no internal relay goroutine that does so.
GetSession, Approve and Cancel for a session id NOT in the in-memory run registry fall back to SessionStore.Load, so a session created (or last persisted) before a restart is still observable and its terminal/awaiting state is loadable. Resuming an in-flight STREAM across a restart is out of scope: the *agent.Run and its event channel live only in process memory, so after a restart there is no run to deliver an approval to. A persisted awaiting session remains loadable (a client can GET it and re-attach), but an Approve/Cancel that finds the session only in the store — with no live run — returns ErrNoActiveRun rather than silently succeeding.
func NewService ¶
NewService validates cfg and constructs a Service using a background startup context. Composition roots with a lifecycle context should call NewServiceContext.
func NewServiceContext ¶ added in v0.0.24
NewServiceContext validates cfg and constructs a Service. ctx bounds and propagates trusted startup context to configured placement providers.
func (*Service) AbandonLearningAttempt ¶ added in v0.0.24
func (s *Service) AbandonLearningAttempt(ctx context.Context, id, expectedVersion string) (*mecatlv1.LearningAttempt, error)
AbandonLearningAttempt performs only the repository's non-compensating CAS transition. It does not roll back or invalidate downstream artifacts.
func (*Service) ActivateLearnedSkill ¶
func (s *Service) ActivateLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*Service) ActiveRuns ¶
ActiveRuns reports the number of sessions this process is actively driving — the count of held session leases (one per live run-entry). It is the "draining: N active runs" figure a graceful shutdown logs after Drain, so the operator can see how many in-flight runs the bounded GracefulStop will cancel. Zero (or no lease wired) means nothing is in flight.
func (*Service) ApplySessionCleanup ¶
ApplySessionCleanup re-plans before mutation, then serializes each deletion by run-entry lock -> maintenance lease -> backend family lock (inside Delete).
func (*Service) ApplySessionMigration ¶
func (s *Service) ApplySessionMigration(ctx context.Context, planID string, batchSize int) (MigrationJob, error)
ApplySessionMigration creates a durable caller-bound job and processes one bounded batch.
func (*Service) Approve ¶
func (s *Service) Approve(ctx context.Context, id session.SessionID, askID string, verdict session.ApprovalVerdict) error
Approve resolves the paused permission ask on the session's in-flight run with the client's three-way verdict (deny / allow-once / allow-always).
SAME-PROCESS path FIRST: a live registered run resolves the ask over its in-memory channel while this process still holds the session lease. On a LookupRun MISS — typically the process that parked the ask died and a different process now serves the Approve — it falls to resumeFromAwaiting (cloud-native Phase 2): if the persisted session is in StateAwaiting it loads the snapshot, rebuilds the engine, re-enters the loop AT the ask, applies the verdict, and drives to completion; the caller relays the returned run's events (the resumed run is registered like any other). A non-awaiting (idle/completed/cancelled/failed) session stays terminal and yields ErrNoActiveRun; an unknown session yields ErrNotFound. The returned run, when non-nil, is the resumed run the wire adapter must drain + FinishRun.
func (*Service) ApprovePlan ¶
func (s *Service) ApprovePlan(ctx context.Context, id session.SessionID, targetMode session.PermissionMode, note string) (<-chan session.Event, error)
ApprovePlan is the atomic plan-approval RPC (issue #206, Wave 4). It resolves a parked PLAN-ORIGINATED permission ask (a PresentPlan call surfaced in plan mode) and — on an ALLOW verdict — starts a FRESH continuation run carrying the harness proceed message, streaming BOTH runs' events on the one returned channel. It composes EXISTING seams and adds NO new engine machinery:
- A live run for the session is rejected (ErrNotAwaitingPlan → 409): an approve mid-run must use the Converse ResumeApproval frame, not this RPC.
- The session is loaded and must be StateAwaiting on a PLAN-ORIGINATED ask (sess.PendingAsk().Origin() == AskOriginPlan); anything else is ErrNotAwaitingPlan. An unknown session is ErrNotFound (via GetSession).
- targetMode → verdict: ModeDefault → VerdictAllowOnce (flip to default), ModeAccept → VerdictAllowAlways (flip to accept-edits), ModePlan/zero → VerdictDeny (iterate, no flip, no continuation run).
- resumeFromAwaiting re-enters the loop AT the ask: Engine.ResumeApproval applies the verdict, the allow paths set r.planApprovedTarget, the run terminates StopPlanApproved, and terminateComplete flips the session mode at the terminal boundary. (The deny path synthesises a deny result and the loop CONTINUES in plan mode — but since this is a fresh resumed run with no further model turns scripted, it ends at StopPlanApproved-less terminal; the session stays in plan mode for the next prompt.)
- ATOMIC CONTINUATION (allow paths only): after the resumed run drains, a FRESH run is started via the SAME StartRunContent path (loadAndReopen → engineAndEnvironmentFor CASE 1 rebuild picks up the FLIPPED mode → execute model) carrying the proceed message agent.PlanApprovedProceedText + an optional operator note. Both runs' events are relayed on the returned channel. On deny, NO continuation run starts (the session stays in plan mode; the model re-plans on the next prompt).
The returned channel carries the MERGED event stream of the resumed run and (on allow) the continuation run, closing once both have ended. The caller (gRPC/HTTP relay) owns the wire discipline: appendEvent per event, skip the three log-only kinds on the client wire, Persist on EvPermissionAsk, and — critically — CANCEL ctx on a send error / client disconnect so this method's internal ctx-watcher cancels the LIVE run (the run is registered in s.runs like any other; this method deregisters it after drain). The caller MUST cancel the passed ctx once it stops draining, or the run can wedge behind a dead relay (mirrors the run.Cancel() the live relays call on disconnect).
func (*Service) ApproveRun ¶
func (s *Service) ApproveRun(ctx context.Context, id session.SessionID, askID string, verdict session.ApprovalVerdict, expectedRunID string) (*agent.Run, error)
ApproveRun is Approve plus the resumed *agent.Run handle (cloud-native Phase 2). On the SAME-PROCESS path (a live registered run) it resolves the ask over the channel and returns (nil, nil): there is no new run, the existing relay delivers the verdict's effects. On the rehydrate path (no live run, the session is awaiting) it returns the freshly-registered resumed run so the caller can relay its events and FinishRun it after the drain. A nil run with a nil error means "the same-process channel handled it; keep relaying the existing stream".
CONCURRENCY: two Approves for the SAME awaiting session that both MISS the live-run fast path must NOT both spawn a resumed run (Engine.ResumeApproval spawns the driving goroutine immediately, so the pending tool would execute twice). The resume decision is therefore serialized per session via s.resumeMu: under the per-session lock the loser re-checks LookupRun, sees the winner's now-registered run, and routes its verdict to that run's channel (the same-process path) — the pending tool runs EXACTLY ONCE. The common live-run case takes the service lock only long enough to order approval against lease-loss invalidation.
WIRE EXPOSURE: the rehydrate-resume path (no live run → resumeFromAwaiting) is reachable only through the HTTP POST /v1/sessions/{id}/approve endpoint, which relays the resumed run as an SSE body (see the HTTP approve handler). The gRPC Converse stream has NO rehydrate path: its ResumeApproval control frame resolves the ask against the stream's OWN live in-process run only (grpc.go readControl), so a gRPC client whose session was evicted has no resume path over Converse and a verdict frame for a dead run is silently dropped. The gRPC rehydrate path is a tracked follow-up (additive, out of the Phase 2 gate) — see docs/adr/0027-cloud-native.md Phase 2.
func (*Service) ArchiveLearnedSkill ¶
func (s *Service) ArchiveLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*Service) BindPlacement ¶ added in v0.0.24
func (s *Service) BindPlacement(ctx context.Context, selector PlacementSelector, operation PlacementOperation) (PlacementBinding, error)
BindPlacement atomically authorizes and resolves a placement through the deployment provider. The caller principal comes only from the authenticated context and the scope only from trusted composition; neither is supplied by the selector or inferred from possession of its opaque ID.
func (*Service) CanProcessSchedule ¶
CanProcessSchedule reports whether a durable schedule is eligible to be claimed by this deployment's scheduler. Rejected legacy state is logged before the scheduler's claim fence so it cannot revive an off-root filesystem path.
func (*Service) Cancel ¶
Cancel cancels the session's in-flight run. The store-fallback semantics match Approve: ErrNotFound when the session is unknown, ErrNoActiveRun when it exists only in the store with no live run.
func (*Service) CancelChild ¶
CancelChild cancels ONE child (a subagent) of the session's in-flight run, addressed by its child session id (the `agentId:` trailer / subagent.start child_id). It mirrors Approve exactly: LookupRun, then the store fallback — ErrNotFound for an unknown session, ErrNoActiveRun for a known-but-runless one. A live run that does not hold the child (unknown id, or the child already finished) yields ErrChildNotFound (HTTP 404; the stream-frame path ignores that race by design instead).
func (*Service) CancelSessionCleanup ¶
CancelSessionCleanup stops future items; committed deletions are not rolled back.
func (*Service) CancelSessionMigration ¶
CancelSessionMigration stops future items without rolling back committed families.
func (*Service) CancelSteer ¶
func (s *Service) CancelSteer(ctx context.Context, id session.SessionID) (agent.SteerOutcome, error)
CancelSteer retracts the session's live run's PENDING (un-drained) steer, reporting the engine's authoritative outcome (retracted / none_pending). It is the Service-level owner of the steer_cancel route — the wire handler drives THIS (mirror of how Cancel routes through the Service), so the live-run lookup stays single-owner and the deferred HTTP/SSE steer endpoint reuses the same Service decision rather than re-deriving it. A steer that already drained at a turn boundary is ordinary recorded history and cannot be retracted (the engine reports none_pending then: the drain won). A session with no live run reports none_pending (there is no inbox to retract from — the steer that would be pending is already lost with its run, the best-effort in-memory contract the docs/acceptance/steer-while-running.md Scenario-2 contract records). The wire's steer_cancel message_id never crosses the Service (the ack-side echo is the caller's own frame field), so the signature stays id-less.
func (*Service) CancelTeammate ¶
CancelTeammate cancels ONE member of a RUNNING team mid-round, via the Supervisor.CancelMember seam (the same per-member cancel the Converse-path registry route fires; BACKGROUND-SUBAGENTS D4, issue #29). The member de-schedules with the cancelled stop reason and its claimed tasks release; the run continues and still delivers its outcome.
The phase is read briefly under Service.mu and released BEFORE touching the supervisor — the same no-lock-across-supervisor discipline as SendTeammateMessage. The teamRunning requirement is ALSO what makes the lock-free CancelMember map read safe: the supervisor's members map is written only by AddMember, and every AddMember serialises BEFORE the teamCreated→teamRunning flip (SpawnTeammate holds the per-team `run` mutex across its phase check + AddMember and rejects once the team has started) — admitting a teamCreated team here would race a concurrent SpawnTeammate's map write. The race on the OTHER side of the gate is benign: RunTeam may finish between the phase read and CancelMember, in which case the late cancel lands as an idempotent no-op (the member's ctx just goes unobserved). Cancelling an already-stopped-but-PRESENT member is likewise an honest no-op (nil success); only an unknown member name returns ErrChildNotFound. A team that is not running (teamCreated / teamDone) returns ErrTeamNotRunning.
func (*Service) CleanupTeam ¶
CleanupTeam drops a created or done team from the registry and frees its slot. Forked member workspaces are torn down by the supervisor when RunTeam returns, so a done team is safe to drop; this releases the registry entry. A team whose phase is teamRunning is NOT dropped — deleting it out from under the in-flight RunTeam would orphan the live supervisor — so it returns ErrTeamRunning (FailedPrecondition) instead. It returns ErrNotFound for an unknown team.
func (*Service) ClearSessionSuccessor ¶ added in v0.0.24
func (s *Service) ClearSessionSuccessor(ctx context.Context, source session.SessionID, placement SuccessorPlacement) (session.SessionID, error)
ClearSessionSuccessor cancels any exact source lifecycle, waits for its relay to deregister, then creates a distinct empty-history peer while preserving source ownership, labels, limits, mode, and exact placement unless a current source-scoped selector is supplied.
func (*Service) ClientMCPFromWire ¶ added in v0.0.22
func (s *Service) ClientMCPFromWire(servers []mcp.ClientServer) (ClientMCPGrant, error)
ClientMCPFromWire is the SINGLE enforcement seam for client-provided MCP servers arriving on a session-creating API request. Both wire transports call it; neither classifies an entry itself.
Order is load-bearing, and it is the ordinary "400 before 501" shape:
- CLASSIFY, unconditionally, through the shared mcp.PartitionClientServers — the same validator the ACP surface uses. A stdio or sse entry is rejected AS SUCH on every deployment, so "No stdio MCP, ever" (AGENTS.md) stays an invariant of the request shape rather than a downstream consequence of a policy flag that some future composition root might flip. Classification is pure string work: it opens no connection and has no side effect, so running it on a deployment that will refuse anyway costs nothing.
- GATE on the deployment policy. A well-formed request that this deployment does not accept is refused with the typed ErrClientMCPUnsupported (UNIMPLEMENTED / 501), never silently dropped — a client whose servers were quietly ignored would run a session it believes has tools it does not have.
An empty list returns an EMPTY grant and no error on EVERY deployment: sending no MCP servers is not a use of the feature, so a TCP deployment must not fail an ordinary create that merely carries an empty repeated field.
It returns a ClientMCPGrant rather than raw specs so that "these specs were classified and permitted" is enforced by the type system: WithClientMCP accepts nothing else, and the grant's field is unexported, so this function is the only place a non-empty one comes from.
Header values never appear in the returned error (they are secret-shaped).
func (*Service) Close ¶
func (s *Service) Close()
Close tears down all per-session engines' MCP managers. It is the Service's shutdown hook so a process exit does not leak any per-session MCP connection. It is safe to call multiple times.
func (*Service) CloseSession ¶
CloseSession tears down the per-session engine registered for id (if any) and removes it from the registry. It is idempotent: an id with no per-session engine is a no-op. The ACP adapter calls it when an editor disconnects so a session's client-provided MCP manager does not outlive the session.
SAFE under an in-flight run (so it needs no run-aware guard like the team path): the close func is the MCP manager's Close, a GRACEFUL shutdown — the underlying go-sdk ClientSession.Close "prevents new requests from being handled, and WAITS for ongoing requests to return" before terminating the connection, and is documented idempotent + concurrency-safe. A disconnect that races a live engine.Run dispatching an MCP tool call therefore does NOT yank the connection mid-call: Close blocks until that CallTool returns (or the jsonrpc2 layer retires it with an error response the remoteTool maps to a model-facing error). The editor disconnect already implies the run is being abandoned, so blocking briefly for the in-flight call to unwind is the correct, leak-free behaviour.
func (*Service) CompactSession ¶ added in v0.0.22
func (s *Service) CompactSession(ctx context.Context, id session.SessionID, caller *session.Principal) (agent.ManualCompactionResult, error)
CompactSession applies one configured compaction pass to an owned main-chat session at a turn boundary. caller is the verified transport principal; it is bound to ctx only when the context has no principal, and a mismatch is rejected. The operation is serialized against run entry and cross-process mutations. A successful no-op neither saves nor appends events.
func (*Service) CompatibilityInfo ¶ added in v0.0.22
func (s *Service) CompatibilityInfo(context.Context) *mecatlv1.GetCompatibilityInfoResponse
CompatibilityInfo returns the deployment's compatibility descriptor (ADR 0248): the API major, the operator-enabled capabilities, the build's supported feature identifiers, and the optional build/deployment labels.
It exists so a client can answer "what is this server?" WITHOUT creating a probe session — ServerCapabilities otherwise rides CreateSessionResponse only, so discovery cost a session that then had to be cleaned up.
The capabilities half REUSES s.capabilities() rather than recomputing a parallel projection. That is the load-bearing part: a second projection would drift from the CreateSession echo, and a client comparing the two would see a server contradicting itself about its own configuration.
The two vocabularies stay SEPARATE by design. capabilities answers "what has this operator enabled?" and changes with operator config; features answers "what does this build implement?" and changes on upgrade. Folding one into the other makes a --no-bash deployment indistinguishable from version skew.
func (*Service) CreateACPSession ¶ added in v0.0.24
func (s *Service) CreateACPSession(ctx context.Context, cwd string, mode session.PermissionMode, limits session.Limits, specs []mcp.ServerConfig, overlay ACPEnvironmentOverlay) (*session.Session, error)
CreateACPSession binds the composition-configured default placement before it consults the ACP cwd assertion. cwd can only confirm that trusted binding; it is never used to construct or select an environment.
func (*Service) CreateSchedule ¶
func (s *Service) CreateSchedule(ctx context.Context, spec port.ScheduleSpec) (port.Schedule, error)
CreateSchedule delegates to the embedded scheduleManager (the single schedule truth, ADR 0076). The manager validates the spec fail-closed, applies the create-seam defaults, computes the first NextFireAt, and Saves. When no manager is wired (the store backs no ScheduleStore) it returns ErrNoScheduleStore — the byte-identical no-scheduling path the gRPC/REST handlers map to Unimplemented.
func (*Service) CreateSession ¶
func (s *Service) CreateSession(ctx context.Context, mode session.PermissionMode, limits session.Limits) (*session.Session, error)
CreateSession allocates a new idle session on the server-owned default placement, persists it, and returns it. An unspecified mode falls back to DefaultMode. It is the no-selector, no-MCP fast path.
func (*Service) CreateSessionWithMCP ¶
func (s *Service) CreateSessionWithMCP(ctx context.Context, mode session.PermissionMode, limits session.Limits, specs []mcp.ServerConfig) (*session.Session, error)
CreateSessionWithMCP creates a session that mounts the client-provided streaming-HTTP MCP servers (specs) for the lifetime of that session, via a PER-SESSION engine. It is the ACP session/new entry for an editor that supplies mcpServers.
- With NO specs it delegates to CreateSession: the session uses the SHARED engine, with zero per-session overhead and no registry entry.
- With specs it REQUIRES Config.SessionEngine (else ErrInvalidArgument: "client MCP not supported"); it builds the per-session engine via that factory, and on success registers it under the new session id so StartRun routes the session's runs to it. A factory error is returned as-is (the caller maps it).
The per-session engine's MCP manager is torn down by CloseSession (editor disconnect) or by the Service's Close.
func (*Service) CreateSessionWithProfile ¶
func (s *Service) CreateSessionWithProfile(ctx context.Context, mode session.PermissionMode, limits session.Limits, sel ProviderSelector, profile SessionProfile, opts ...CreateSessionOption) (*session.Session, error)
CreateSessionWithProfile creates a session bound to an optional non-default provider/model selector AND a tool-surface profile (issue #55), with no client MCP. It is the gRPC/HTTP entry for a CreateSession request. The zero selector + default profile delegates to the shared-engine fast path; a non-zero selector OR the no-fs profile REQUIRES Config.SessionEngine (else ErrInvalidArgument) and resolves through the factory (an unknown/unavailable provider id surfaces as ErrInvalidArgument). Setting ModelID with an empty ProviderID is rejected (a bare model on the env-derived default provider is ambiguous). Placement is bound exclusively from the server-owned default or explicit no-FS profile.
opts is the variadic options pattern (CreateSessionOption): WithSessionID overrides the minted id (ADR 0059 decision #7 Phase-2 — the scheduler fire path mints a "sched--"-prefixed id). Zero opts is byte-identical to the pre-Phase-2 signature.
func (*Service) CreateSessionWithProvider ¶
func (s *Service) CreateSessionWithProvider(ctx context.Context, mode session.PermissionMode, limits session.Limits, sel ProviderSelector) (*session.Session, error)
CreateSessionWithProvider creates a session bound to a non-default provider/model selector (multi-provider Phase 0, S3) via a PER-SESSION engine, with no client MCP and the DEFAULT profile.
func (*Service) CreateTeamForSession ¶ added in v0.0.24
func (s *Service) CreateTeamForSession(ctx context.Context, source session.SessionID, name, goal string, maxTeamTokens int, members []agent.MemberSpec) (string, []team.Member, error)
CreateTeamForSession creates a team in an owning session's exact authorized environment. The caller supplies no path or selector; no-FS is never upgraded.
func (*Service) CreateTeamOnDefaultPlacement ¶ added in v0.0.24
func (s *Service) CreateTeamOnDefaultPlacement(ctx context.Context, name, goal string, maxTeamTokens int, members []agent.MemberSpec) (string, []team.Member, error)
CreateTeamOnDefaultPlacement allocates a new agent team over the trusted default placement, enrols an optional initial roster, and returns its server-assigned id together with the enrolled roster. It builds the supervisor (binding the member-engine factory to the shared team), then adds each member before the team is registered.
Enrolment is ATOMIC: if any member fails to enrol the whole team is abandoned — it is never registered (no partial team leaks) and does not consume a MaxTeams slot. The supervisor's AddMember already cleans up a member's own fork on its own failure, and an un-registered supervisor (with whatever members it did add) is simply garbage-collected. The failing member's error is classified via classifyAddMemberErr. members may be empty: the team is created empty and the client can still SpawnTeammate before RunTeam.
maxTeamTokens is the per-request team-wide token budget, folded into the server-configured Config.TeamTokenBudget TIGHTEN-ONLY at create time (agent.TightenTeamTokenBudget, issue #36): a non-positive value inherits the server budget; a positive value applies only when it is lower.
It returns ErrTeamsDisabled when teams are not enabled. CreateTeamOnDefaultPlacement is the explicit trusted-composition entry for a team that is not derived from an existing session.
func (*Service) DecideDream ¶
func (s *Service) DecideDream(ctx context.Context, id string, decision DreamDecision) (DreamReceipt, error)
DecideDream applies or dismisses one retained opaque plan. No operation material is accepted from the caller, so the authoritative retained plan is the only mutation input.
func (*Service) DecideLearningProposal ¶
func (s *Service) DecideLearningProposal(ctx context.Context, id, expected, decision, reason, project string) (*mecatlv1.LearningProposal, error)
DecideLearningProposal rejects or approves a staged proposal using version CAS.
func (*Service) DeleteSchedule ¶
DeleteSchedule delegates to the embedded scheduleManager. Idempotent (the store's Delete discipline).
func (*Service) DeleteSession ¶
DeleteSession physically removes an owned main session and all store-managed sidecars. Absence and foreign ownership are both idempotent success, preventing deletion from becoming an ownership oracle. Infrastructure failures remain loud.
func (*Service) DeleteSessionForRetention ¶
DeleteSessionForRetention removes one session selected by the composition-owned automatic-retention policy. Unlike DeleteSession it is not restricted to main chats, but it still serializes against run entry, acquires the cross-process mutation lease, and revalidates durable taxonomy and lifecycle after acquiring that lease. It is an internal composition callback, never a wire operation.
func (*Service) DeleteSessionForRetentionCandidate ¶
func (s *Service) DeleteSessionForRetentionCandidate(ctx context.Context, candidate port.SessionDiscoveryMeta) error
DeleteSessionForRetentionCandidate removes one exact planner candidate while keeping the mandatory maintenance/run-entry lease exclusions held through the backend's atomic final metadata comparison and family deletion. Automatic and manual retention intentionally use the same exclusion posture.
func (*Service) Diagnostics ¶
func (s *Service) Diagnostics() port.Diagnostics
Diagnostics returns the operational diagnostics sink the Service was configured with. It is the read-side accessor composition (the scheduler's FireFunc) uses to WARN on a non-fatal degradation (e.g. a carried-context prior-session-load failure that degrades to fresh-context). A NopDiagnostics is returned when none was wired (the constructor guarantees non-nil, so this is belt-and-suspenders).
func (*Service) DiffLearnedSkillVersions ¶
func (s *Service) DiffLearnedSkillVersions(ctx context.Context, request *mecatlv1.DiffLearnedSkillVersionsRequest) (*mecatlv1.DiffLearnedSkillVersionsResponse, error)
func (*Service) Drain ¶
func (s *Service) Drain()
Drain arms the drain gate (ADR 0048, mecak8s): subsequent run-entries (StartRunContent / resumeFromAwaiting via acquireLease) are rejected with ErrUnavailable so a shutting-down replica stops accepting new runs and a rolling update steers traffic to a survivor. It is idempotent and safe to call from a signal handler or the /drain HTTP endpoint. In-flight runs are NOT cancelled here — that is the bounded GracefulStop's job in the cmd binary; Drain only gates new entries. The gate is one-way: there is no un-drain (a draining replica is retiring).
func (*Service) EmitScheduleEvent ¶
func (s *Service) EmitScheduleEvent(ctx context.Context, payload session.SchedulePayload)
EmitScheduleEvent appends a SchedulePayload as an EvSchedule* event to the fire session's durable EventLog. It is the composition-injected emit callback the scheduler invokes (via scheduler.Config.EmitScheduleEvent) for fired/failed/ skipped fires. For v1 delivery is durable-log-only (pull-only via GetFire/ListFires); a live broadcast stream is a future phase. A skipped fire (no session id) is dropped from the durable log (the log is session-keyed) and surfaces only via the operator diagnostic.
It routes through the ONE appendEvent chokepoint so the lifecycle events are stamped with the acting caller exactly like the fire's run events — the scheduler's system principal for a tick fire, the requester for a manual FireNow (ADR 0204 decision 5: every durable append path stamps). ctx is the caller's; it is cancel-detached here so a fire's finished/cancelled ctx cannot abort the durable append, while its VALUES (the principal) survive.
func (*Service) EndSession ¶
EndSession is the precondition-checked sibling of CloseSession: the surface-facing session-end entry for the gRPC/HTTP transports (the ACP adapter calls the void CloseSession directly on disconnect). It verifies the session exists and serializes against run admission. A locally registered run owns the session until its relay calls FinishRun, so close fails with ErrFailedPrecondition without releasing the lease or tearing down any local engine, policy, or environment. A persisted awaiting snapshot with no local run is not active ownership: teardown releases local resources while leaving the durable PendingAsk untouched. Closing an already-released (but still persisted) session remains idempotent. EndSession never deletes the persisted snapshot and never cancels a run; cancellation is an orthogonal operation.
func (*Service) FinishRun ¶
FinishRun removes run from the in-flight registry for id. It is the EXPORTED counterpart of register that every wire adapter must call (typically via `defer`) once it has finished draining run.Events(), so a completed run does not leak in the registry. It is idempotent and only removes the entry if run is still the one recorded (a later run for the same session is never clobbered), so it is safe to call unconditionally after a drain.
func (*Service) FireNow ¶
FireNow delegates to the embedded scheduleManager. The manager distinguishes the no-scheduler states: a nil manager (no ScheduleStore at all) → ErrNoScheduleStore here; a non-nil manager with no scheduler wired → ErrSchedulerNotRunning (the manager's own FireNow). A wired scheduler's sentinels (ErrScheduleDisabled / ErrFireNowOverlap / ErrScheduleExhausted / ErrScheduleNotLeader) are mapped by the manager.
func (*Service) ForkSessionSuccessor ¶ added in v0.0.24
func (s *Service) ForkSessionSuccessor(ctx context.Context, req ForkSuccessorRequest) (session.SessionID, error)
ForkSessionSuccessor creates a history-carrying peer on inherited exact placement or a freshly matched source-scoped worktree.
func (*Service) GenerateDream ¶
func (s *Service) GenerateDream(ctx context.Context, target DreamTarget) (DreamReview, error)
GenerateDream creates an opaque, bounded-lifetime review plan for a closed target.
func (*Service) GetFire ¶
GetFire delegates to the embedded scheduleManager, which resolves and authorizes the stored physical parent key before returning the fire.
func (*Service) GetLearnedSkill ¶
func (s *Service) GetLearnedSkill(ctx context.Context, request *mecatlv1.GetLearnedSkillRequest) (*mecatlv1.GetLearnedSkillResponse, error)
func (*Service) GetLearningAttempt ¶ added in v0.0.24
func (s *Service) GetLearningAttempt(ctx context.Context, id string) (*mecatlv1.LearningAttempt, error)
GetLearningAttempt returns one content-free projection from the verified caller's private attempt partition.
func (*Service) GetLearningProposal ¶
func (s *Service) GetLearningProposal(ctx context.Context, id, project string) (*mecatlv1.LearningProposal, error)
GetLearningProposal returns one proposal from the caller's selected partition.
func (*Service) GetMcpPrompt ¶
func (s *Service) GetMcpPrompt(ctx context.Context, server, name string, args map[string]string) (mcp.PromptResult, error)
GetMcpPrompt expands a named prompt with args on the named server. server and name must be non-empty; a nil provider yields ErrNoMCPProvider; an unknown server name yields ErrInvalidArgument; an unknown prompt, missing required arg, or other expansion fault on a known server yields ErrInternal.
func (*Service) GetSchedule ¶
GetSchedule returns an owned schedule by name. A mismatch deliberately has the same port-level not-found result as an absent schedule.
func (*Service) GetSession ¶
GetSession returns the persisted session under id, or ErrNotFound.
Absence, foreign ownership, and a broken store are deliberately ONE caller-visible answer, so a caller cannot probe for another owner's ids. That concealment is owed to the CALLER only: an infrastructure failure is logged for the operator, because otherwise a storage outage is indistinguishable from mass deletion from both sides at once. A genuine not-found is the normal case and stays silent.
func (*Service) GetSoul ¶
GetSoul returns the resolved soul (persona) snapshot (the build-time projection injected via Config.Soul). It is a pure read of that snapshot; no live re-read. When no soul source is wired it returns an empty snapshot (present=false), never nil, so the wire adapters always have a SoulInfo to serialize.
func (*Service) GetTranscript ¶
func (s *Service) GetTranscript(ctx context.Context, id session.SessionID) (*SessionTranscript, error)
GetTranscript performs exactly one ownership-checked SessionStore load and returns its current Conversation. It deliberately bypasses run entry: no environment resolution, engine rehydration, lease, or persistence occurs.
func (*Service) GetUserModel ¶
GetUserModel returns the CURRENT user-model entries (a live read of the wired lister) plus aggregate size + hash over the rendered "key — description" rows. A nil lister (user model disabled) yields an empty response. A store fault is returned as ErrInternal so the wire adapters surface it distinctly.
func (*Service) GetUserModelDetail ¶
func (s *Service) GetUserModelDetail(ctx context.Context, key string) (*mecatlv1.UserModelDetail, error)
GetUserModelDetail returns one exact entry without exposing a mutation path. Base-only listers honestly return nil detail.
func (*Service) GracefulDrain ¶ added in v0.0.24
GracefulDrain settles locally-owned runs after closing admission. Executing runs are cancelled and must be joined by their relay (FinishRun) before their terminal aggregate is persisted and their lease is explicitly released. Awaiting runs are cancelled only in memory: preserveDurable prevents the relay from replacing the already-persisted PendingAsk handoff point. A context timeout invalidates local mutation capability and stops renewal, but never explicitly releases an unjoined run's lease; TTL then governs takeover.
func (*Service) HasScheduler ¶
HasScheduler reports whether a scheduler was wired into the embedded scheduleManager. It is the read-side companion to SetScheduler: nil-safe (the byte-identical default wires no scheduler, and a store with no ScheduleStore has no manager at all).
func (*Service) IsDraining ¶
IsDraining reports whether the drain gate is armed. It is the read-side companion to Drain: a cmd binary's dynamic ReadyFunc (mecak8s /readyz) closes over it so readiness flips to not-ready the moment Drain is armed, without coupling the probe to the Service's internal atomic. It is the ReadyFunc's read; ActiveRuns is the "how many in flight" figure. Safe to call from a signal handler / HTTP handler goroutine.
func (*Service) IsLive ¶
IsLive reports whether a top-level Service run or an engine-owned delegation child is currently in flight in this process. The two registries share one predicate so stale reconciliation and every destructive maintenance path see the same process-local exclusion. Cross-process liveness is protected by the separately configured SessionLease.
func (*Service) LeaseSweepDisabled ¶
LeaseSweepDisabled reports whether SessionStale has stickily disabled the staleness sweep for the process lifetime (an ErrLeaseUnsupported backend). Exported for internal/app's Step 4 sweep to check before scanning, mirroring SessionStale/IsLive/Diagnostics.
func (*Service) ListAgents ¶
ListAgents returns the resolved agent-definition snapshot (possibly empty). It is a pure read of the injected snapshot; no live discovery.
func (*Service) ListCommandsForSession ¶ added in v0.0.24
func (s *Service) ListCommandsForSession(ctx context.Context, id session.SessionID) ([]Command, error)
ListCommandsForSession owner-authorizes and exactly reattaches before command discovery. A no-FS source returns empty without touching either provider.
func (*Service) ListLearnedSkills ¶
func (s *Service) ListLearnedSkills(ctx context.Context, request *mecatlv1.ListLearnedSkillsRequest) (*mecatlv1.ListLearnedSkillsResponse, error)
func (*Service) ListLearningAttempts ¶ added in v0.0.24
func (s *Service) ListLearningAttempts(ctx context.Context, stateValue, cursor string, limit int) (*mecatlv1.ListLearningAttemptsResponse, error)
ListLearningAttempts returns one bounded page from the verified caller's private attempt partition. The repository's opaque next ID is the cursor.
func (*Service) ListLearningProposals ¶
func (s *Service) ListLearningProposals(ctx context.Context, statusValue, cursor string, limit int, project string) (*mecatlv1.ListLearningProposalsResponse, error)
ListLearningProposals returns one bounded partition page.
func (*Service) ListMcpPrompts ¶
ListMcpPrompts returns the prompt snapshots for server (empty = union of all servers). Returns nil with no provider configured.
func (*Service) ListMcpResources ¶
ListMcpResources returns the resource snapshots for server (empty = union of all servers). Returns nil with no provider configured.
func (*Service) ListMcpSources ¶
func (s *Service) ListMcpSources(ctx context.Context) []source.SourceInfo
ListMcpSources returns the MCP source inventory (possibly empty). When a MCPSourceProber is configured it RE-CONSULTS the resolved sources for live status/diagnostics on every call (so a client refresh reflects current state, not the startup snapshot); on a prober that returns nil it falls back to the cached startup snapshot so the panel always renders. With no prober it is a pure read of the injected snapshot (no live discovery).
func (*Service) ListModels ¶
ListModels returns the resolved selectable-model inventory snapshot (possibly empty) — every available provider's catalog models, secret-free. When an on-demand refresher is installed (issue #262, R1.4) it is invoked FIRST (self-guarded: it decides which providers are stale and enforces its own cooldown) so a provider that just came back up is reflected on THIS call, with no restart; with no refresher installed (the byte-identical default) this is a pure read of the injected snapshot, exactly as before.
func (*Service) ListSchedules ¶
ListSchedules filters before returning the collection so an ownerless or foreign record cannot affect caller-visible list metadata.
func (*Service) ListSessionPage ¶
func (s *Service) ListSessionPage(ctx context.Context, request ListSessionsPageRequest) (ListSessionsPage, error)
ListSessionPage returns one bounded keyset page. The optional pager is a deployment capability: unsupported stores fail honestly instead of falling back to an unbounded response. Ownership criteria are sent to the store so filtering occurs before page formation and TotalCount.
func (*Service) ListSessions ¶
func (s *Service) ListSessions(ctx context.Context) ([]SessionSummary, error)
ListSessions returns the stored-session inventory — the picker metadata a client renders to let an operator open an EXISTING session by id (issue #245 Phase 1). It is backed by port.PrunableStore.List (type-asserted on the configured store); a store that does not implement PrunableStore, or one that returns ErrPruneUnsupported, degrades to an EMPTY slice — never an error — so a no-persistence/cloud server honestly reports "no sessions".
Each row carries only picker metadata (id, timestamps, state, turn count, model id); NO conversation content is loaded. For each PrunableStore row the service best-effort loads the snapshot to populate State/Turns/CreatedAtUnix and ModelID from the session's own PERSISTED sess.ModelID (NOT Service.ResolvedModel, which falls back to the shared default engine's model for a non-live session — that would misreport every session that was ever run on a non-default model); a Load failure leaves those fields zeroed but still returns the row (a corrupt snapshot file is surfaced in the picker with its id/mtime, so the operator can see it exists even if it can't be opened). Rows are sorted most-recently-active first (modified_at descending). Read-only.
Cost: each row does a Store.Load (jsonlstore: reads the last snapshot line). Acceptable for a picker; no pagination in Phase 1.
FAST PATH: when the store implements port.MetaLister (jsonlstore does), ListSessions uses MetaList — a CHEAP last-line read that skips the full conversation — instead of a full Load per row. This keeps listing N sessions O(N × last-line-read) rather than O(N × filesize) for large histories. The MetaLister path is the same latest-line-wins source Load trusts; a store that does NOT implement MetaLister falls back to the Load-per-row path (correct, just slower; memstore/redisstore/grpcdriver use it until they implement MetaList). The Title from MetaList is the snapshot Title ONLY — the lazy deriveTitle fallback (walking the conversation) is NOT available on the fast path; a session whose Title was never seeded shows "" on the fast path. That is acceptable for a picker (the snapshot Title is seeded by the loop on the first genuine prompt, so the common case is populated).
func (*Service) ListSkillChanges ¶
func (s *Service) ListSkillChanges(ctx context.Context, r *mecatlv1.ListSkillChangesRequest) (*mecatlv1.ListSkillChangesResponse, error)
func (*Service) ListSkills ¶
ListSkills returns the current skills inventory (possibly empty).
func (*Service) ListTeam ¶
func (s *Service) ListTeam(ctx context.Context, teamID string) ([]team.Member, []team.Task, bool, error)
ListTeam returns the team roster, the shared task list, and whether the team has reached quiescence.
func (*Service) ListToolHiveGroups ¶
ListToolHiveGroups derives the distinct, non-empty ToolHive groups from the inventory (the live re-probe when a MCPSourceProber is set, else the startup snapshot — see liveSources). It considers only sources whose Kind is "toolhive". Output is sorted for deterministic results.
func (*Service) ListWorktreesForSession ¶ added in v0.0.24
func (s *Service) ListWorktreesForSession(ctx context.Context, id session.SessionID) ([]ScopedWorktree, error)
ListWorktreesForSession owner-authorizes and exactly reattaches before enumeration, then issues caller/source-scoped selectors without retaining them. A no-FS source is an empty result and invokes no lister.
func (*Service) LoadACPSession ¶ added in v0.0.24
func (s *Service) LoadACPSession(ctx context.Context, id session.SessionID, cwd string, specs []mcp.ServerConfig, overlay ACPEnvironmentOverlay) (*session.Session, error)
LoadACPSession owner-authorizes the snapshot, reattaches its exact persisted EnvironmentRef, and only then checks cwd. It never binds a current default or constructs an environment from cwd.
func (*Service) LoadSession ¶
LoadSession resumes a previously-persisted session so a subsequent StartRun continues it. It loads the latest snapshot from the store and, if the session is in a terminal state, REOPENS/INTERRUPTS/RECOVERS it to StateIdle (preserving the conversation history) and re-persists, so the next prompt's BeginTurn is legal. A cleanly COMPLETED session is reopened via Reopen; a CANCELLED session (an interrupted turn) is recovered via Interrupt, which also repairs the history (closing out any orphaned tool calls); a FAILED session (a transient provider failure) is recovered via Recover, the same history repair (issue #51) — recovery makes retry possible, not guaranteed. A session already idle is returned unchanged.
It returns ErrNotFound when the store has no snapshot for id (including the in-memory store after a process restart, or when no store-dir is configured and the id was never created in this process).
func (*Service) LoadSessionWithMCP ¶
func (s *Service) LoadSessionWithMCP(ctx context.Context, id session.SessionID, specs []mcp.ServerConfig) (*session.Session, error)
LoadSessionWithMCP resumes a previously-persisted session AND re-mounts the client-provided streaming-HTTP MCP servers (specs) for the lifetime of that session, via a PER-SESSION engine. It is the ACP session/load entry for an editor that re-supplies mcpServers on resume — the symmetric sibling of CreateSessionWithMCP (session/new).
- With NO specs it delegates to LoadSession: the resumed session uses the SHARED engine, with zero per-session overhead and no registry entry.
- With specs it REQUIRES Config.SessionEngine (else ErrInvalidArgument: "client MCP not supported"); it loads + reopens-if-completed FIRST (so an unknown id fails fast — ErrNotFound — without a wasted MCP connect), then builds the per-session engine via that factory and, on success, registers it under the session id so StartRun routes the session's runs to it. A factory error is returned as-is (the caller maps it).
The per-session engine's MCP manager is torn down by CloseSession (editor disconnect) or by the Service's Close.
func (*Service) LookupRun ¶
LookupRun returns the in-flight run for a session and true, or false if no run is currently registered for it.
func (*Service) LookupSteerMessageID ¶
LookupSteerMessageID returns the WATERMARK message_id for a drained bundle — the LATEST (tail) id of the session's ordered pending list — and CONSUMES the whole list (the bundle drained; the next bundle starts a fresh list). The client splits its ordered queue on the watermark (positional, never text-match — the drift class the abandoned `_ string` parameter gestured at). "" when the list is empty (an id-less steer, or a drain after a retract).
func (*Service) MaintenanceMutationAvailable ¶
MaintenanceMutationAvailable reports whether destructive maintenance can currently obtain the required process/cross-process exclusion. Composition consults it before starting automatic retention and again before each sweep.
func (*Service) ManualDreamCapabilities ¶
func (s *Service) ManualDreamCapabilities() DreamCapabilities
ManualDreamCapabilities returns a defensive copy of the composition snapshot.
func (*Service) MaybeAutoApprovePlan ¶
MaybeAutoApprovePlan fires the plan-mode auto-approve (issue #206 Wave 6a) when the Service observes a parked plan-approval ask on a headless deployment. It is called by the gRPC/HTTP relay loops alongside appendEvent+Persist for every EvPermissionAsk, and by tests simulating the relay. It is a NO-OP unless ALL of the following hold:
- cfg.PlanModeAutoApprove is true (the OPT-IN operator flag — DEFAULT OFF).
- The ask is plan-originated (session.AskOriginPlan — a PresentPlan call).
- The deployment is headless (no interactive human approver — the engine's Deps.Interactive is false). An interactive deployment surfaces the ask to the human instead; auto-approve must NOT pre-empt a human.
When all three hold it auto-resolves the ask via the EXISTING ApprovePlan path (ModeDefault + a loud note), emitting a LOUD diagnostic so the operator sees that NO HUMAN reviewed the plan. It NEVER fires for a non-plan ask (a policy/ hook ask is still the human's/auto-deny's responsibility), NEVER fires interactively, and is NEVER load-bearing for safety (the engine still gates the PresentPlan — this merely resolves the parked ask).
func (*Service) OwnershipEnforced ¶
OwnershipEnforced reports whether caller ownership is active. Composition uses it only where ownerless persisted metadata must fail closed before reconstructing a caller context; resource decisions still flow through ownsResource/authorizeSession.
func (*Service) PauseSchedule ¶
PauseSchedule delegates to the embedded scheduleManager. The manager calls SetEnabled — the dedicated atomic flag update — because Save CANNOT mutate Enabled (Save preserves the existing State half on a Spec overwrite).
func (*Service) Persist ¶
Persist saves the current state of the session backing id, if a run is registered for it. It is the seam the adapters call when a run enters the awaiting state (so a persisted awaiting session is loadable for re-attach after a restart) and at run end (so the terminal state is durable). The engine mutates the session in place, so this captures whatever state it is in now. A best-effort no-op when no run is registered.
When the session is StateAwaiting, Persist also marks the runState.awaiting flag (race-free for Close's cancel loop). The flag is the signal Close uses to EXCLUDE a parked-awaiting run from the shutdown cancel loop: cancelling such a run would overwrite the durable awaiting snapshot (the cloud-native Phase 2 resume point) with cancelled. Persist is called by the relay/test AFTER observing an event (EvPermissionAsk → loop parked, or EvResult → loop done), so reading sess.State here races no concurrent loop write (the loop is parked or exited; the state write happened-before the event the caller observed). The flag is a server-layer atomic, never read by the engine loop.
func (*Service) PlanSessionCleanup ¶
func (s *Service) PlanSessionCleanup(ctx context.Context, scope CleanupScope) (CleanupPlan, error)
PlanSessionCleanup performs no writes. The explicit management gate runs before the store-wide pager, so tenants cannot form pages or aggregate counts.
func (*Service) PlanSessionMigration ¶
func (s *Service) PlanSessionMigration(ctx context.Context) (MigrationPlan, error)
PlanSessionMigration performs no writes. The opaque plan handle binds the authenticated principal to the inspected storage generation; apply turns it into a separate durable job handle.
func (*Service) ProviderCapabilities ¶
func (s *Service) ProviderCapabilities() port.ProviderCapabilities
ProviderCapabilities reports the DEFAULT provider+model's multimodal input support, so a surface adapter can advertise it (e.g. ACP promptCapabilities) and loud-reject unsupported prompt content. It returns the composition-computed DefaultCapabilities — the catalog ∩ adapter INTERSECTION for the default provider+cfg.Model — NOT the bare engine.Capabilities() (adapter-only, which would over-advertise a model the adapter can transmit to but the catalog says cannot take image). This is the SAME value the CreateSessionResponse echoes for a default-engine session, so the ACP gate and the wire echo cannot disagree.
ACP carries NO per-session provider/model selector in P0 (session/new passes only mcpServers, never a selector), so every ACP session rides the DEFAULT engine and the Agent's capture-once a.caps = svc.ProviderCapabilities() is correct for every ACP session. A per-session ACP capability gate lands only when an ACP selector lands (P1+) — see docs/adr/0016-multi-provider.md.
func (*Service) ProviderStatuses ¶
func (s *Service) ProviderStatuses() []*mecatlv1.ProviderStatus
ProviderStatuses returns the current per-provider live-listing status snapshot (never nil after NewService). ListModels' gRPC/HTTP callers thread it onto ListModelsResponse.provider_status alongside the model list.
func (*Service) PublishSessionEvent ¶
PublishSessionEvent fans the event to every subscriber registered for the given session id. It is NON-BLOCKING: a full subscriber channel drops the event (the subscriber is dead/disconnected — drain-to-discard without wedging the producer). Events published here are the SAME events the run's own Events() channel carries (projection equivalence); the subscriber receives the raw session.Event, never a proto type. The loop stays storage-agnostic — it never calls this; the relay or the delivery driver (deliverFireResult) publishes.
func (*Service) ReadMcpResource ¶
func (s *Service) ReadMcpResource(ctx context.Context, server, uri string) (mcp.ResourceContents, error)
ReadMcpResource reads a single resource by URI from the named server. server and uri must be non-empty; a nil provider yields ErrNoMCPProvider; an unknown server name yields ErrInvalidArgument; a read/transport fault on a known server yields ErrInternal.
func (*Service) ReattachPlacement ¶ added in v0.0.24
func (s *Service) ReattachPlacement(ctx context.Context, ref session.EnvironmentRef) (PlacementBinding, error)
ReattachPlacement authorizes and resolves the exact persisted environment identity. It never invokes Bind and therefore cannot follow a changed default.
func (*Service) ReattachPlacementInScope ¶ added in v0.0.24
func (s *Service) ReattachPlacementInScope(ctx context.Context, ref session.EnvironmentRef, scope string) (PlacementBinding, error)
ReattachPlacementInScope requires the durable schedule scope to match this deployment before reauthorizing and resolving the exact persisted ref.
func (*Service) RecoverNotice ¶
RecoverNotice returns the pre-flight advisory message for session id when the last loadAndReopen recovered a PERMANENTLY-failed session. It returns "" when there is no pending notice (the common case: a freshly-created session, a transient failure, or a follow-up prompt on the same recovered session). Each notice is consumed on the first call — a subsequent call for the same id returns "" — so the advisory is emitted ONCE per recovery and never repeats.
It is called by the relay adapters (gRPC Converse, HTTP relayRunSSE) right after StartRunContent to inject an EvRecoverNotice synthetic event BEFORE the main event loop, so the client sees the warning before the provider call burns tokens on the same unrecoverable error.
func (*Service) ReflectSession ¶
func (s *Service) ReflectSession(ctx context.Context, id session.SessionID) (*mecatlv1.ReflectionReceipt, error)
ReflectSession explicitly reflects a completed caller-owned session.
func (*Service) RejectLearnedSkill ¶
func (s *Service) RejectLearnedSkill(ctx context.Context, r *mecatlv1.MutateLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*Service) RenameSession ¶
func (s *Service) RenameSession(ctx context.Context, id session.SessionID, title string) (*session.Session, error)
RenameSession applies an explicit operator title change to an owned main session. Authorization, kind/state/liveness checks, and lease acquisition are serialized under the same per-session mutex used by prompt starts.
func (*Service) ResolvedModel ¶
func (s *Service) ResolvedModel(id session.SessionID) ResolvedModel
ResolvedModel reports the EFFECTIVE provider+model for the session under id: the per-session engine's precomputed resolved model when a per-session engine is registered (a non-default provider/model selector or client MCP), else the composition-computed DefaultResolvedModel (the shared/default-engine path). It backs the CreateSessionResponse.resolved_model echo. Mirrors SessionCapabilities verbatim.
MODE→MODEL RE-EMIT (ADR 0030 Layer 3): the identity is read from the REGISTERED per-session engine (se.providerID/se.modelID). After a plan↔execute switch triggers the run-entry rebuild (engineAndEnvironmentFor swaps the registered engine on the next StartRun), ResolvedModel AUTOMATICALLY returns the re-resolved model — no change here. The ORDERING is deliberate: a SetMode response echoes the still-CURRENT (pre-rebuild) model (the model is fixed per turn; the rebuild happens at the NEXT run-entry, not on the SetMode call itself), so a client reads the new model from GetSession or the next CreateSession-style echo AFTER the mode-changed turn begins — never mid-turn.
The provider/model IDENTITY never recomputes within a single engine generation (the per-session-engine value was computed ONCE in composition; the default-path ids are the baked DefaultResolvedModel ids), so the wire echo cannot drift from the engine the session actually runs on. The ContextWindow SCALAR is resolved LIVE-FIRST at call time for BOTH branches via the injected Config.ResolveContextWindow (so a GetSession after the live model-catalog swap reflects the live window, not the curated-catalog floor a live-only model lacks) — mirroring the live-first modality input SessionCapabilities already consumes. The injected resolver is the ECHO resolver (echoWindowResolver), which differs from the engine's resolve-at-use Deps.ContextWindow in ONE deliberate way: while the one-shot live refresh is still in flight it returns a PROVISIONAL 0 for a live-only model not yet in the catalog (the client treats 0 as "refetch on turn-end" — the issue #66 footer-heal gate), whereas the engine always floors to 128k (it can never run on a 0 window). This provisional 0 is DISTINCT from "resolver not wired (nil)": nil ⇒ no window scalar at all (the identity-only ResolvedModel); a wired resolver returning 0 is the honest "live answer not in yet" signal. Post-completion the resolver floors an uncatalogued model to 128k, so the echo settles and the heal gate closes (no-network boundedness).
func (*Service) ResumeSchedule ¶
ResumeSchedule delegates to the embedded scheduleManager. See PauseSchedule.
func (*Service) ResumeSessionMigration ¶
func (s *Service) ResumeSessionMigration(ctx context.Context, jobID string, batchSize int) (MigrationJob, error)
ResumeSessionMigration processes the next bounded batch of a durable job.
func (*Service) RetryFailedRun ¶
RetryFailedRun resumes the failed model step from the persisted conversation state without submitting another prompt. Live system instructions and operator context are resolved again for the retry. The caller owns draining the returned run and calling FinishRun, exactly as for StartRunContent. Eligibility is derived exclusively from the persisted typed terminal metadata and is consumed only after every fallible setup step has succeeded and the recovered idle snapshot has been saved.
func (*Service) RetryLearningAttempt ¶ added in v0.0.24
func (s *Service) RetryLearningAttempt(ctx context.Context, id, expectedVersion string) (*mecatlv1.LearningAttempt, error)
RetryLearningAttempt performs only the repository's failed-to-queued CAS transition. Scheduling and downstream artifacts are deliberately untouched.
func (*Service) RollbackLearnedSkill ¶
func (s *Service) RollbackLearnedSkill(ctx context.Context, r *mecatlv1.RollbackLearnedSkillRequest) (*mecatlv1.MutateLearnedSkillResponse, error)
func (*Service) RunTeam ¶
func (s *Service) RunTeam(ctx context.Context, teamID string, sink func(agent.TeamEvent)) (agent.TeamOutcome, error)
RunTeam drives the team to quiescence, invoking sink for every member event, and returns the outcome. It blocks for the team's lifetime; the gRPC handler runs it on the request goroutine and forwards events to the stream.
Like the per-session run-entry funnel (acquireLease), RunTeam honours the drain gate (ADR 0048, mecak8s): once Drain is armed a draining replica refuses a NEW team run BEFORE the phase flip / team claim so a shutting-down pod steers team traffic to a survivor. An in-flight RunTeam is NOT cancelled by Drain itself (that is the bounded GracefulStop's job). The gate starts false — byte-identical default when Drain has not been called.
func (*Service) ScheduleManager ¶
func (s *Service) ScheduleManager() port.ScheduleManager
ScheduleManager returns the consumer-local port.ScheduleManager the model-facing Schedule tool (ADR 0073) drives: the embedded scheduleManager (the single truth, ADR 0076) — NOT the Service itself. It returns nil UNLESS the configured Store backs a port.ScheduleStore (the manager constructor returns nil in that case) — the SAME conditional gate the capabilities echo (Scheduling) uses, so the tool registration and the capability bit agree and a store-less deployment gets the honest absent-tool path, never a stub. Composition calls it AFTER NewService (the manager is constructed before buildEngine and handed to the Service via Config.ScheduleManager, or self-constructed by the Service from cfg.Store for the legacy test path) and injects the result into the catalog assets for the Schedule tool's registration.
func (*Service) SendTeammateMessage ¶
SendTeammateMessage posts a message into a member's inbox, delivered at that member's next turn boundary. An empty from defaults to the reserved operator identity (team.OperatorSender); a non-empty from is authenticated by team.Send, which rejects any value that is neither a current member nor the operator (ErrUnknownSender → InvalidArgument) so the wire path cannot forge a sender.
func (*Service) SessionCapabilities ¶
func (s *Service) SessionCapabilities(id session.SessionID) port.ProviderCapabilities
SessionCapabilities reports the resolved input capability (catalog ∩ adapter) for the session under id: the per-session engine's precomputed neutral caps when a per-session engine is registered (a non-default provider/model selector or client MCP), else the composition-computed DefaultCapabilities (the shared-engine path). The value was computed ONCE in composition (modelCapability) and stored; SessionCapabilities never recomputes it, so the wire echo cannot drift from the ListModels view. It backs the CreateSessionResponse.session_capabilities echo.
func (*Service) SessionCleanupJob ¶
SessionCleanupJob returns one caller-bound sanitized maintenance projection.
func (*Service) SessionMigrationJob ¶
SessionMigrationJob returns a caller-bound sanitized durable job projection.
func (*Service) SessionStale ¶
SessionStale reports whether meta's persisted StateRunning snapshot is a crash-orphan (issue #475) rather than a genuinely in-flight run. It decides; it does not write — SettleIfStale performs the actual repair once a caller has decided a candidate is stale. Exported for internal/app's composition- level sweep (Step 4) to consume, mirroring IsLive/Diagnostics.
The order matters and mirrors internal/adapter/scheduler/scheduler.go's shouldReconcileStaleFire/isPriorFireLive (age gate first, lease as a secondary refinement, trial lease released immediately, never held across a write) — an earlier draft of this fix inverted that order and is why the design history in the accompanying plan calls this out explicitly:
- Age horizon (staleSessionWindow) is a HARD PRECONDITION: a fresh snapshot is never stale, no matter what liveness/lease signals say.
- IsLive(id): a same-process live run is never stale.
- If a port.SessionLease is wired, a bounded TRIAL Acquire is the secondary refinement: - ErrLeaseHeld, but s.heldLeases[id] shows THIS process already holds the REAL lease for id: that is the self-held-lease correction — a run that died without releasing its own lease is evidence of staleness, not liveness. Treat it as stale. - ErrLeaseHeld otherwise (a genuinely different, live owner holds it): not stale. - success (nobody held it): release the trial immediately (this function only decides; it never holds a lease across the caller's later write) and report stale. - ErrLeaseUnsupported: sticky-disable the WHOLE sweep for the process lifetime (see LeaseSweepDisabled) rather than silently falling back to local-only liveness for this one candidate — the fallback would reintroduce the exact cross-replica unsoundness the lease branch exists to prevent, for the one backend where this error is actually reachable. Report not stale. - any other error/timeout: fail-safe, not stale.
- No lease wired at all: age + IsLive is the complete policy — a not-live, past-window candidate IS stale (the single-process/file- storage default path).
func (*Service) SetMode ¶
func (s *Service) SetMode(ctx context.Context, id session.SessionID, mode session.PermissionMode) (*session.Session, error)
SetMode changes the permission posture of the session under id and persists the change, returning the updated session. It is the out-of-band mode-switch seam (ACP session/set_mode). It applies to the LIVE session when a run is registered (so the change takes effect immediately for an idle-between-prompts session held in the registry) and otherwise to the stored snapshot.
It returns ErrNotFound for an unknown session, ErrInvalidArgument for an empty mode, and propagates session.ErrIllegalTransition (wrapped as ErrInvalidArgument) when the session is mid-turn (running/awaiting) — the aggregate refuses a mode change while a turn is in flight, so the caller must defer it to the next prompt. A change to the mode the session already has is a no-op success.
func (*Service) SetModels ¶
SetModels atomically swaps the selectable-model inventory. It is the composition layer's seam for the background live-catalog refresh: Build seeds the embedded snapshot synchronously (via Config.Models) and, once the live fetch completes, calls SetModels with the merged result. ListModels and the ModelSelection capability then reflect the new set on their next read. It is concurrency-safe (atomic store) and carries only the projected proto slice — no registry/catalog/ lister type crosses this boundary. A nil argument stores an empty (non-nil) slice so the pointer is never nil.
func (*Service) SetModelsRefresher ¶
SetModelsRefresher installs the OPTIONAL on-demand model-list refresher (issue #262, R1.4: "proxy started after boot ⇒ models appear on next /models open, no restart"). ListModels calls it (bounded — the refresher owns its own timeout/cooldown/which-providers-are-stale logic) before returning its snapshot. nil (the default — set once in Build, only when at least one intent-driven provider exists) makes ListModels a pure snapshot read, byte-identical to every deployment without this feature.
func (*Service) SetProviderStatus ¶
func (s *Service) SetProviderStatus(status []*mecatlv1.ProviderStatus)
SetProviderStatus atomically swaps the per-provider live-listing status (issue #262). It is the composition layer's seam: the Build-time probe supplies the initial value, and the background + on-demand live-model refreshes re-project it on every re-fetch. Mirrors SetModels exactly (a nil argument stores an empty, non-nil slice so the pointer is never nil).
func (*Service) SetScheduleMinInterval ¶
SetScheduleMinInterval injects the scheduler cadence floor the create-seam enforces (ADR 0073, AC1.3). Delegated to the embedded manager: the floor guards the SHARED validateScheduleSpec — the Schedule tool's create AND the REST/gRPC create — whether or not the tick loop runs (a --no-scheduler deployment still manages schedules manually). 0 disables the floor. Called once by composition before serving. No-op when no manager is wired (no schedule create-seam to guard).
func (*Service) SetScheduler ¶
SetScheduler wires a scheduler onto the embedded scheduleManager (the late-bind seam, ADR 0076). It is the delegated setter: composition builds the scheduler AFTER NewService (the FireFunc closes over the Service) and attaches it here; the manager holds the atomic scheduler pointer FireNow reads. Nil-safe (no manager wired → no-op, the byte-identical no-scheduling path). The Service's own s.mu is no longer involved (the manager's atomic pointer is the single truth); Close/Drain read s.schedMgr.HasScheduler() instead of a Service-held scheduler field.
func (*Service) SetSessionEnvironment ¶
func (s *Service) SetSessionEnvironment(id session.SessionID, env tool.Environment)
SetSessionEnvironment registers a per-session Environment OVERRIDE for id, so a subsequent StartRun uses env as the COMPLETE execution environment (Workspace + optional bound CommandRunner + accurate ref) instead of building one from the shared factories. It is the seam the ACP adapter uses to route a session's file I/O through the editor's fs/* buffers: the ACP adapter constructs a complete shell-less Environment (a real-filesystem Workspace rooted at the session cwd with a local ref, but NO command runner — the editor provides no shell) and registers it here. A second call for the same id replaces the override. The override is evicted by CloseSession (and drained by Close), so the caller MUST pair it with CloseSession on the owning connection's teardown (the ACP adapter tracks the session and does this on disconnect). The gRPC/HTTP surfaces never call this, so their environment path is unchanged (issue #462 phase-2 finding #2).
func (*Service) SettleIfStale ¶
SettleIfStale repairs a session id that a caller has ALREADY decided is stale (via SessionStale): it loads the raw snapshot, re-checks State==StateRunning and !IsLive(id) (closing the TOCTOU between whatever decided staleness and this load — the snapshot may have moved on since, or a genuinely live run may have started in the gap), and if it is genuinely still running and not locally live, abandons it via Session.Abandon() and persists the repair. It performs NO staleness decision of its own.
This is the SWEEP's (Step 4) repair path ONLY: the sweep discovers a candidate id from a metadata scan with no in-memory session for it, so it must Load fresh from the store. A caller that already holds an in-memory *session.Session (Step 3's run-entry funnel, `loadAndReopen`) must NOT call this function — repairing the on-disk copy via a fresh Load would leave the funnel's OWN in-memory sess (already loaded, about to be handed to engine.Run) untouched and still carrying its unpaired tool_use, so the HTTP-400 this whole fix exists to prevent would survive unnoticed. The funnel instead calls sess.Abandon() + Store.Save directly on the session it already holds.
Returns whether it actually settled something (false, nil is the honest no-op result for a session that already moved on, e.g. a race with a genuinely live re-entry or a peer's own settle).
func (*Service) SpawnTeammate ¶
func (s *Service) SpawnTeammate(ctx context.Context, teamID string, spec agent.MemberSpec) (team.Member, error)
SpawnTeammate enrols a member in a team (before RunTeam) and returns its roster entry. A Mutating member requires a configured Forker. It is rejected with ErrTeamRunning once the team has started running: AddMember mutates the Supervisor's unsynchronised member maps, which the in-flight RunTeam is reading.
func (*Service) StaleRunningCandidates ¶
StaleRunningCandidates returns only the metadata needed by the dedicated stale-session reconciler. It is store-wide infrastructure enumeration, not a caller-owned list operation, and accepts only that worker's system root.
func (*Service) StartRun ¶
func (s *Service) StartRun(ctx context.Context, id session.SessionID, text string) (*agent.Run, error)
StartRun loads the session, builds its workspace, starts a run on the shared engine and registers the *agent.Run so Approve/Cancel can reach it. The caller is responsible for draining run.Events() AND, once the channel closes, for calling FinishRun(id, run) to remove the run from the registry (each wire adapter `defer`s FinishRun after the drain — see grpc.go/http.go/the ACP adapter). It returns ErrNotFound if the session does not exist.
func (*Service) StartRunContent ¶
func (s *Service) StartRunContent(ctx context.Context, id session.SessionID, text string, parts []session.Content) (*agent.Run, error)
StartRunContent is the multimodal sibling of StartRun: it starts a run with a prompt carrying flattened text PLUS non-text media parts (image/audio). text may be "" when parts carries the content; at least one of text/parts must be non-empty (else ErrInvalidArgument). StartRun delegates here with nil parts. The media passes through to the engine untouched — command expansion and the UserPromptSubmit hook operate on the TEXT only (see Engine.Run). All other behaviour (workspace/engine selection, registration, drain contract) is identical to StartRun.
It reopens-if-completed (via loadAndReopen) so a follow-up prompt on a session that cleanly finished a prior turn continues it — the in-process multi-turn counterpart to the cross-process LoadSession resume path. StartRunContent is the public chat-purpose multimodal sibling of StartRun. Only explicitly-stamped main sessions are admitted; delegation children, scheduled sessions, unknown metadata, and every historical child/fire prefix fail closed. The trusted scheduler uses StartScheduledRunContent instead.
func (*Service) StartScheduledRunContent ¶
func (s *Service) StartScheduledRunContent(ctx context.Context, id session.SessionID, text string, parts []session.Content) (*agent.Run, error)
StartScheduledRunContent is the trusted scheduler-purpose entry. It admits explicitly-stamped scheduled sessions and the historical sched-- fallback for legacy unknown snapshots. It is intentionally absent from public transports; scheduler composition calls it directly.
func (*Service) Steer ¶
func (s *Service) Steer(ctx context.Context, id session.SessionID, text string, parts []session.Content, messageID, expectedRunID string) (agent.SteerOutcome, bool, *agent.Run, error)
Steer routes an operator steer (mid-run injected input, issue #512) for a session to the right home. It is the Service-level routing decision the wire-facing steer handler drives: the steer NEVER drops silently.
- LIVE run: if a run is registered AND its steer inbox still accepts it, the text enqueues to that run's steer inbox (the run drains it at the next turn boundary) and the function reports the engine's authoritative outcome (accepted / appended) with promoted=false.
- LOST TERMINAL RACE: no live run, or the live run's inbox already closed (the engine reported too_late — the run went terminal behind the caller's "still running" belief): the steer is PROMOTED into a fresh follow-up run through the EXISTING hardened run-entry funnel — StartRunContent (loadAndReopen + the lease + recover-if-completed / interrupt-if-cancelled / recover-if-failed / abandon-if-crash-orphaned-running) — exactly like a normal follow-up prompt, and reported as (agent.SteerTooLate, true).
The returned promotedRun (non-nil only when promoted) is the registered follow-up run the caller must drain + FinishRun, exactly as StartRunContent's caller does. An unknown session id yields ErrNotFound (via the funnel); a terminal-state repair failure surfaces as the funnel's error.
messageID is the client-minted correlation id of the Steer frame ("" when the caller supplied none). On an accepted steer it parks in the session's FIFO so the EvSteer drain echo can echo it (LookupSteerMessageID); the ACK-side echo is the caller's own frame field (it never crosses the Service).
func (*Service) StorageHealth ¶
func (s *Service) StorageHealth(ctx context.Context) (StorageHealth, error)
StorageHealth returns aggregate storage measurements only after management authorization. Unsupported backends are represented as unavailable data, not measured zero and not an error.
func (*Service) StorageReady ¶
StorageReady reports whether the session store is reachable. It is the readiness probe the cmd binary's ReadyFunc closes over, so /readyz tests the SAME store the Service serves traffic through (not a second client opened in the binary). A non-pinging store (memstore, jsonlstore, grpcdriver — no Ping method) is treated as always ready; a Redis store's Ping determines readiness. The caller should bound ctx (e.g. 2s) so a stalled backend fails the probe quickly rather than wedging readiness.
func (*Service) StreamSessionEvents ¶
func (s *Service) StreamSessionEvents(ctx context.Context, id session.SessionID) (iter.Seq2[session.Event, error], error)
StreamSessionEvents replays a session's durable event log as a lazy iterator over the recorded events (cloud-native Phase 3a read-back). It is the service-layer surface over port.EventLog.Read that the gRPC/HTTP handlers stream to a client opening an existing session (issue #245 Phase 1).
A nil EventLog (no durable log configured) returns ErrNoEventLog so the wire adapters map to UNIMPLEMENTED (HTTP 501) — honestly reporting the surface is absent rather than pretending an unknown id. An unknown/pruned session id yields an EMPTY iterator (absence is data): port.EventLog.Read is defined to return an empty stream for an unknown id, so the service surfaces that verbatim. The loop stays storage-agnostic — this method never starts a run or makes a model call. Read-only.
The returned iterator yields the events the relay PERSISTED — including the three log-only kinds (EvApproval/EvCompactionArchive/EvUserPrompt) a LIVE Converse relay skips on the client wire. The caller (the gRPC/HTTP handler) relays ALL of them: a client opening a PAST session wants the verdicts and user prompts, as they ARE the transcript. They are already metadata-only / redacted by construction (gauntlet #7: no raw args/deny-reason bodies/child content ever cross), so no extra filter applies at this layer.
func (*Service) Subscribe ¶
func (s *Service) Subscribe(ctx context.Context, id session.SessionID) (<-chan session.Event, func(), error)
Subscribe registers a new per-session live event subscriber and returns a receive-only channel of session.Event plus an unsubscribe function. Every call to PublishSessionEvent for the given session id fans the event to ALL currently- registered subscribers. The channel carries a buffer of 64 events (matching a Run's own event buffer). When the channel is full, PublishSessionEvent DROPS the event (non-blocking drain-to-discard — a dead client never wedges the producer). The returned unsubscribe func is IDEMPOTENT (safe to call more than once, e.g. an explicit call plus a deferred one): it removes this subscription and closes the channel exactly once so the subscriber goroutine can exit cleanly.
Subscribe's sole entry point is the gRPC StreamSessionLive wire handler — an UNTRUSTED boundary, not a trusted in-process caller. When OwnershipEnforced is set it authorizes via GetSession (issue #368) before registering a subscriber, so a caller who cannot load the session cannot observe its live events either; the check mirrors StreamSessionEvents exactly.
func (*Service) UndoLearningPromotion ¶
func (s *Service) UndoLearningPromotion(ctx context.Context, id, expected, project string) (*mecatlv1.LearningProposal, error)
UndoLearningPromotion compensates the current linked promotion using version CAS.
func (*Service) UpdateSchedule ¶
func (s *Service) UpdateSchedule(ctx context.Context, spec port.ScheduleSpec) (port.Schedule, error)
UpdateSchedule delegates to the embedded scheduleManager. The manager re-validates the spec (the shared create-seam) and overwrites the Spec half while preserving the State half (firing progress) + the CreatedAt timestamp.
func (*Service) WatchSessionEvents ¶ added in v0.0.22
func (s *Service) WatchSessionEvents(ctx context.Context, id session.SessionID, after port.Cursor, runID string) (iter.Seq2[WatchEnvelope, error], error)
WatchSessionEvents is the DURABLE replay-then-follow read: it replays a session's log from after the given cursor, announces the transition, and follows the tail until the caller stops (issue #821, ADR 0250).
It is ONE operation on purpose. The two existing read paths cannot be composed into it without a hole: port.EventLog.Read is a complete durable replay with no position and no follow, and Service.Subscribe is a live in-memory registry with no history and no durability — so "read everything, then subscribe" silently loses whatever was appended between the two steps. A cursor closes that window, because the follow resumes from exactly where the replay stopped.
Both transports (gRPC WatchSessionEvents, SSE GET /v1/sessions/{id}/watch) consume THIS method, which is what makes their envelope sequences identical rather than merely similar (AC7.3). Put transport framing in the handlers and delivery semantics here.
The returned iterator yields at most one error, as its last item, per the port.EventLog convention. A caller that breaks out early releases the watch.
func (*Service) WithAuthorizedSession ¶
func (s *Service) WithAuthorizedSession(ctx context.Context, id session.SessionID, effect func(*session.Session) error) (*session.Session, error)
WithAuthorizedSession serializes a caller-owned side effect with session run entry. It performs an ownership-only preflight before taking caller-selected coordination, then reloads and reauthorizes under runEntryMu immediately before effect. The callback must not call another operation that locks the same session id.
type SessionEngineFactory ¶
type SessionEngineFactory func(ctx context.Context, sel ProviderSelector, specs []mcp.ServerConfig, profile SessionProfile, workspace string, mode session.PermissionMode) (SessionEngineResult, error)
SessionEngineFactory builds a PER-SESSION agent engine over a non-default provider/model selector AND/OR the client-provided streaming-HTTP MCP servers (specs), returning a SessionEngineResult (engine + per-session capabilities + close func) and an error. It is the seam the ACP adapter uses to mount an editor's session/new mcpServers AND the seam the gRPC/HTTP CreateSession path uses to bind a per-session provider/model — both WITHOUT leaking those tools (or the registry/catalog) into the shared engine every other session uses. A session needing BOTH a non-default model and client MCP gets ONE engine over ONE catalog from a single call (sel + specs are orthogonal inputs). The factory returns an error wrapping ErrInvalidArgument for an unknown/unavailable provider id. The composition root (internal/app) supplies it via Config.SessionEngine; when nil, a non-default selector or non-empty specs are rejected with ErrInvalidArgument. It mirrors MemberEngineFactory: the Service references the type in its signatures but never builds managers itself.
profile is the session's tool-surface profile (issue #55), flowing exactly as the selector does: ProfileDefault keeps today's catalog byte-identical; ProfileNoFS makes the factory assemble the NO-FILESYSTEM catalog (no file tools, no Bash, no Parallel, no SkillDraft; file-less Subagent/Team children) and apply the no-FS prompt posture. A no-FS session ALWAYS routes through this factory — the shared engine has the FS tools baked in.
workspace is the SESSION's workspace root (issue #32): the factory pins the per-session engine's CHILD permission resolver to it, so a per-session engine's subagents/members/branches resolve project permission rules from THEIR session's pre-fork base root — never the server flag's root, and never a fork root. Empty (a no-fs session, or a resume that persisted none) pins no project root (user/CLI rules only).
mode is the session's PermissionMode (ADR 0030 Layer 3, the mode→model re-resolution): when it is session.ModePlan the composition factory re-resolves the engine's model through the `plan` slot (within the SAME provider — the provider stays fixed per session), so a planning turn runs on a strong-reasoning model and an executing turn on the session model (the opusplan pattern). ModeDefault/ModeAccept keep the session-selected model (byte-identical). The factory echoes the mode back as SessionEngineResult.BuiltForMode so the Service can detect a stale engine across a mode change and rebuild via THIS same path (the run-entry/rehydration seam), never resolving a model itself.
type SessionEngineResult ¶
type SessionEngineResult struct {
// Engine is the built per-session engine. Required (non-nil on a nil error).
Engine *agent.Engine
// Capabilities is the session's resolved input capability (catalog ∩ adapter),
// computed in composition. The server echoes it verbatim; it never recomputes.
Capabilities port.ProviderCapabilities
// ProviderID/ModelID are the EFFECTIVE provider+model IDENTITY this session
// resolved to (the empty-selector default, an explicit selector, or a passthrough
// id), computed ONCE in composition from the SAME resolved locals that feed the
// engine — the server echoes them verbatim on CreateSessionResponse.resolved_model
// and never recomputes. Same single-source discipline as Capabilities. The context
// WINDOW is NOT a frozen field: Service.ResolvedModel resolves it live-first at echo
// time via Config.ResolveContextWindow (the SAME source the engine reads), so the
// echo and the running engine agree after a live-catalog swap with no rebuild.
ProviderID string
ModelID string
// ReasoningEffort is the EFFECTIVE, normalised + per-provider-clamped reasoning
// effort this session resolved to (ADR 0055): "" when unset (provider default),
// else the neutral token actually sent to the adapter (e.g. openai + "max" echoes
// "high"). Computed ONCE in composition from the SAME resolved value that re-mints
// (or reuses) the engine's adapter; the server echoes it verbatim on
// CreateSessionResponse.resolved_model and never recomputes — the SAME single-
// source discipline as ProviderID/ModelID.
ReasoningEffort string
// BuiltForMode is the session PermissionMode the factory RESOLVED THE MODEL FOR
// (ADR 0030 Layer 3, the mode→model re-resolution). The factory echoes back the
// mode it was handed — the SAME single-source discipline as ProviderID/ModelID —
// so the Service can stamp sessionEngine.builtForMode from this one value and later
// detect a stale engine (sess.Mode != se.builtForMode) without re-resolving any
// model itself. The empty value (a factory that predates the mode axis) is
// session.ModeDefault-equivalent: the Service treats "" as "no mode pin" and the
// stale check degrades to never-rebuild-on-mode (byte-identical to pre-Phase-3).
BuiltForMode session.PermissionMode
// DebugMCPTools is the exact direct-tool ceiling resolved by a debug factory.
// It contains model-facing tool names only and is persisted on the session.
DebugMCPTools []string
// MountedClientMCP names the client-provided MCP servers that ACTUALLY
// CONNECTED for this session — never an echo of what was requested. It is nil
// when no specs were passed, and SHORTER than the request when some server was
// unreachable (mcp.NewManager keeps only successful connections).
//
// It exists because connecting is best-effort in composition and that is the
// right default for ONE of the two callers, not both. The ACP adapter wants a
// usable session even when an editor's MCP server is down; a gRPC/HTTP
// CreateSession caller cannot see composition's WARN and would otherwise be
// handed a session ID for a session missing tools it asked for, with no way to
// detect it. So the factory reports the mounted set and the Service enforces
// all-or-nothing on the wire path only (see verifyClientMCPMounted).
//
// A factory reached through the WIRE path MUST populate it. Leaving it empty
// while servers were requested is treated as "nothing mounted" and fails the
// create, rather than as "no claim made" — a guarantee a factory can silently
// opt out of by forgetting a field is not a guarantee. Only WithClientMCP
// (the wire-only option) turns the check on, so the ACP path and every
// selector-only factory are unaffected.
MountedClientMCP []string
// Close tears down the session's MCP manager. Never nil (a no-op when no specs).
Close func() error
}
SessionEngineResult is what a SessionEngineFactory returns: the built per-session engine, the per-session resolved input Capabilities (a NEUTRAL port.ProviderCapabilities computed in composition as the catalog ∩ adapter intersection for the session's resolved provider+model — see internal/app modelCapability), and the Close func that tears down that session's MCP manager (a no-op when no specs). A struct (not a 4-tuple) keeps the two interface-typed members readable and leaves room for future per-session metadata without another signature churn. The Service echoes Capabilities back on CreateSessionResponse (session_capabilities) and never recomputes it — the composition is the single source so the wire echo and the ListModels view cannot disagree.
type SessionInventoryActionReasons ¶
type SessionInventoryActionReasons struct {
PublicChat CapabilityReason
Inspect CapabilityReason
CopyID CapabilityReason
ViewTranscript CapabilityReason
Fork CapabilityReason
Rename CapabilityReason
Delete CapabilityReason
}
SessionInventoryActionReasons carries one closed reason for each disabled action.
type SessionInventoryCapabilities ¶
type SessionInventoryCapabilities struct {
PublicChat bool
Inspect bool
AuthoritativeTranscript bool
ActivityReplay bool
CopyID bool
ViewTranscript bool
Fork bool
Rename bool
Delete bool
}
SessionInventoryCapabilities is the proto-free action posture for one row.
type SessionMutationCapability ¶ added in v0.0.24
type SessionMutationCapability struct {
// contains filtered or unexported fields
}
SessionMutationCapability is the process-local proof that this Service may start a durable mutation for a session. It is deliberately not a backend fencing token: admission and invalidation are atomic locally, while a backend call admitted before invalidation may finish.
func NewSessionMutationCapability ¶ added in v0.0.24
func NewSessionMutationCapability(enabled bool) *SessionMutationCapability
NewSessionMutationCapability constructs the local mutation gate. When leasing is not configured, enabled is false and the gate is a byte-identical pass-through.
func (*SessionMutationCapability) Disable ¶ added in v0.0.24
func (c *SessionMutationCapability) Disable()
Disable permanently turns the capability into the no-lease pass-through mode. It is used when an optional lease backend reports ErrLeaseUnsupported.
func (*SessionMutationCapability) Grant ¶ added in v0.0.24
func (c *SessionMutationCapability) Grant(id session.SessionID)
Grant marks id as owned by this process.
func (*SessionMutationCapability) GuardStore ¶ added in v0.0.24
func (c *SessionMutationCapability) GuardStore(next port.SessionStore) port.SessionStore
GuardStore gates engine-owned snapshot saves at operation admission. Loads are read-only and pass through. Creation remains on Service's unwrapped store path.
func (*SessionMutationCapability) GuardToolCallRecorder ¶ added in v0.0.24
func (c *SessionMutationCapability) GuardToolCallRecorder(next port.ToolCallRecorder) port.ToolCallRecorder
GuardToolCallRecorder drops audit-sidecar mutations that begin after local lease capability invalidation. The recorder port has no error return.
func (*SessionMutationCapability) Invalidate ¶ added in v0.0.24
func (c *SessionMutationCapability) Invalidate(id session.SessionID)
Invalidate denies new mutations for id after ownership loss.
func (*SessionMutationCapability) Remove ¶ added in v0.0.24
func (c *SessionMutationCapability) Remove(id session.SessionID)
Remove forgets a normally released capability. It is safe only after every owner using the capability has settled.
type SessionMutationClass ¶ added in v0.0.24
type SessionMutationClass uint8
SessionMutationClass classifies application code that can touch a durable session family. It is deliberately separate from caller authorization: ownership is proved before coordination, while this inventory records the coordination required before storage mutation.
const ( // SessionMutationLeaseOwned acquires the session lease after runEntryMu and // keeps it through the backend call. SessionMutationLeaseOwned SessionMutationClass = iota // SessionMutationLeaseProven runs only for a registered run whose Service-owned // session lease is already held; lease loss invalidates that proof. SessionMutationLeaseProven // SessionMutationNewFamily is the creation-only exception: a caller-owned // identity is published with atomic create/reservation before any session can // have an existing lease owner. Every later mutation uses the lease classes. SessionMutationNewFamily // SessionMutationReadOnly reads session state without changing its family. SessionMutationReadOnly // SessionMutationComposition changes process-local composition state only. SessionMutationComposition )
type SessionMutationEntry ¶ added in v0.0.24
type SessionMutationEntry struct {
Class SessionMutationClass
Rationale string
}
SessionMutationEntry is one reviewed source-level inventory row.
type SessionProfile ¶
type SessionProfile string
SessionProfile is the session's TOOL-SURFACE profile, enum-as-string on the wire (CreateSessionRequest.profile — the Event.type/Result.stop idiom: a string field, additive values, no proto enum). It is a NEUTRAL value object owned by the server adapter, like ProviderSelector: the composition root (internal/app) interprets it when building the per-session engine; the adapter only validates and routes it. The profile is FIXED for the session lifetime.
const ( // ProfileDefault is the full filesystem profile. Composition binds the // server-owned deployment default before constructing this tool surface. ProfileDefault SessionProfile = "" // ProfileNoFS is the NO-FILESYSTEM profile (issue #55): no workspace, no // file tools (Read/Edit/Write/Grep/Glob), no Bash, no Parallel, no // SkillDraft — the agent works through MCP tools, memory, web fetch, Skill // bodies, and file-less delegation. It binds an exact no-FS EnvironmentRef and // requires a per-session engine because the shared engine has FS tools. ProfileNoFS SessionProfile = "no-fs" )
func ParseSessionProfile ¶
func ParseSessionProfile(s string) (SessionProfile, error)
ParseSessionProfile validates a wire profile string. "" is the default profile; "no-fs" is the no-filesystem profile; anything else is a loud ErrInvalidArgument — never a silent fallback to the default profile (an operator asking for a constrained surface must not silently get the full one).
type SessionSummary ¶
type SessionSummary struct {
// SessionID is the stored session's id.
SessionID string
// ModifiedAtUnix is the last-write timestamp in Unix seconds (the
// PrunableStore row mtime; the sort key for the picker).
ModifiedAtUnix int64
// State is the persisted lifecycle state (idle/running/awaiting/completed/...).
// Empty when the snapshot could not be loaded (a corrupt store row still
// surfaces its id/mtime).
State string
// Turns is the persisted model-call count. Zero when the snapshot could not
// be loaded.
Turns int
// ModelID is the resolved model id this session ran on (bare string, no
// provider context). Empty when the session never resolved a model or the
// snapshot could not be loaded.
ModelID string
// CreatedAtUnix is the creation timestamp in Unix seconds. Zero when the
// snapshot could not be loaded.
CreatedAtUnix int64
// Title is the human-readable session label (seeded once from the first
// genuine user prompt, clamped to 120 runes). Populated from the snapshot
// Title, or — when that is empty — from the lazy deriveTitle fallback
// (walks the conversation for the first genuine user prompt). Empty for a
// session with no genuine prompt.
Title string
// TitleProvenance reports whether the title is prompt-derived, operator-authored,
// or legacy/unknown.
TitleProvenance session.TitleProvenance
// Placement is bounded display-only placement metadata.
Placement session.PlacementMetadata
// Owner is the verified caller the session is attributed to.
Owner *session.Principal
// Kind and Relationship are the durable trusted-producer taxonomy.
Kind session.SessionKind
Relationship session.SessionRelationship
// Capabilities and Reasons describe each public action valid for this row.
// ReasonCode is the legacy aggregate public-chat reason.
Capabilities SessionInventoryCapabilities
Reasons SessionInventoryActionReasons
ReasonCode CapabilityReason
}
SessionSummary is one stored session's picker metadata — id, timestamps, state, turn count, and the resolved model id. It carries NO conversation content: it is the cheap row a client renders in an "open existing session" picker. The Service exposes its own proto-free type so the wire adapters (toProtoSessionSummaries) and any in-process consumer need not import the proto package. `model_id` is a bare opaque string (NOT a full ResolvedModel) to keep the picker row cheap and provider-neutral.
type SessionTranscript ¶
type SessionTranscript struct {
SessionID session.SessionID
Messages []session.Message
Complete bool
Activity ActivityReplayStatus
Kind session.SessionKind
Relationship session.SessionRelationship
}
SessionTranscript is the coherent, human-displayable projection of one loaded session aggregate. A successful load is complete even when Messages is empty.
type StorageHealth ¶
type StorageHealth struct {
port.SessionStorageHealth
Ownerless OwnerlessCutoverInventory
Policy RetentionPolicy
LastSweep time.Time
LastSweepAvailable bool
NextSweep time.Time
NextSweepAvailable bool
ActiveJob string
LastFailure string
}
StorageHealth is the authenticated, content-free management projection.
type StorageMaintenanceEvent ¶
type StorageMaintenanceEvent struct {
Kind, Key string
State StorageMaintenanceState
Failure string
Resumable bool
}
StorageMaintenanceEvent contains only closed kinds, opaque internal keys, and stable sanitized failures. It must never carry backend errors or paths.
type StorageMaintenanceState ¶
type StorageMaintenanceState string
StorageMaintenanceState is a closed lifecycle vocabulary shared with the composition-owned health projection.
const ( // StorageMaintenanceStarted marks an active execution or durable reattachment. StorageMaintenanceStarted StorageMaintenanceState = "started" // StorageMaintenanceProgress refreshes an active job after a checkpoint. StorageMaintenanceProgress StorageMaintenanceState = "progress" // StorageMaintenanceCompleted removes a successfully terminal job. StorageMaintenanceCompleted StorageMaintenanceState = "completed" // StorageMaintenanceCancelled removes a forward-cancelled job. StorageMaintenanceCancelled StorageMaintenanceState = "cancelled" // StorageMaintenanceFailed records a sanitized failure; Resumable controls activity. StorageMaintenanceFailed StorageMaintenanceState = "failed" )
type StorageMaintenanceStatus ¶
type StorageMaintenanceStatus struct {
LastSweep, NextSweep time.Time
LastSweepAvailable, NextSweepAvailable bool
ActiveJob, LastFailure string
}
StorageMaintenanceStatus is the content-free lifecycle of retention, migration, and cleanup. ActiveJob is a deterministic comma-separated set of closed job kinds (with counts for concurrent same-kind jobs).
type SuccessorPlacement ¶ added in v0.0.24
SuccessorPlacement selects exact inheritance when empty or one freshly matched source-scoped worktree when Selector is present.
type UserModelEntry ¶
type UserModelEntry struct {
// Key is the entry's stable key.
Key string
// Description is the entry's one-line description.
Description string
}
UserModelEntry is the surface-agnostic listing metadata for one user-model fact (key + description, value omitted), mirroring tool.MemoryEntry's index shape. The Service exposes its own type so the wire adapters and the composition seam (UserModelLister) need not import the memory adapter.
type UserModelInspector ¶
type UserModelInspector interface {
Inspect(ctx context.Context, key string) (tool.MemoryRecord, bool, error)
}
UserModelInspector is the optional exact-entry detail capability. Keeping it separate preserves base-only/old remote list compatibility.
type UserModelLister ¶
type UserModelLister interface {
// List returns the user-model entries, key-sorted, or an error on a genuine
// store fault.
List(ctx context.Context) ([]UserModelEntry, error)
}
UserModelLister enumerates the CURRENT user-model entries (key + description, value omitted). It is the composition-injected seam backing GetUserModel: the composition root closes over the user-model store's Index so a fetch reflects the live store state. It is read-only.
type WatchEnvelope ¶ added in v0.0.22
type WatchEnvelope struct {
// Event is the recorded event, or nil on a phase-only frame.
Event *session.Event
// Cursor is the opaque resume token positioned AFTER this envelope.
//
// It is SCOPED TO THE run_id IT WAS ISSUED UNDER. A filtered watch advances
// its internal position over records the filter dropped, so a cursor handed
// back on a DIFFERENT filter — or on none — resumes past events that filter
// would have delivered, silently. Resume with the same run_id, or start over
// from the beginning.
Cursor port.Cursor
// Phase is one of the WatchPhase* values, as an open string.
Phase string
}
WatchEnvelope is ONE unit of watch delivery: what happened, where the client now is, and which phase of the watch it arrived in.
Event is nil on a PHASE-ONLY frame. There are exactly two: the single replay→live transition marker, and every gap. Both are delivery facts with no event behind them, which is why the phase — not a synthetic event — carries them.
type WorkspaceFactory ¶
WorkspaceFactory builds the session-scoped tool.Workspace for a session root. The server is workspace-agnostic: the composition root injects memfs (tests) or osfs (production) via this seam.
type Worktree ¶
type Worktree struct {
// Path is the absolute working-tree path (the value handed to
// CreateSessionRequest.workspace to bind a session to this worktree).
Path string
// Branch is the checked-out ref name; empty for a detached HEAD.
Branch string
// Head is the commit SHA the worktree is at.
Head string
// Bare is true for a bare worktree.
Bare bool
}
Worktree is one discovered git worktree of a repo, mirroring the proto Worktree message (a `git worktree list --porcelain` record). The Service exposes its own proto-free type so the wire adapters and the composition seam (WorktreeLister) need not import the proto package directly.
type WorktreeLister ¶
type WorktreeLister interface {
// List returns the worktrees of the repo at root (the main worktree first, in
// `git worktree list` order), or an error on a genuine discovery fault. An
// untrusted/non-repo root yields an empty slice, NOT an error (fail-soft).
List(ctx context.Context, root string) ([]Worktree, error)
}
WorktreeLister enumerates the git worktrees of the repo rooted at root. It is the composition-injected discovery seam backing ListWorktrees (the client's /worktrees overlay): the composition root supplies an osfs-backed implementation that shells out to `git worktree list --porcelain` with a scrubbed env, trust-gated; a no-FS/cloud deployment (or an untrusted workspace) leaves it nil. It is read-only and nil-safe: when nil, ListWorktrees returns an empty list and ServerCapabilities.worktrees is false. It NEVER performs a live model/network call and never mutates anything.
type WorktreeSelectorIssuer ¶ added in v0.0.24
type WorktreeSelectorIssuer struct {
// contains filtered or unexported fields
}
WorktreeSelectorIssuer issues opaque process-local selectors without keeping a registry. Matching recomputes every candidate against current inventory.
func NewWorktreeSelectorIssuer ¶ added in v0.0.24
func NewWorktreeSelectorIssuer(key []byte) (*WorktreeSelectorIssuer, error)
NewWorktreeSelectorIssuer constructs an issuer from one random process key.
func (*WorktreeSelectorIssuer) Issue ¶ added in v0.0.24
func (i *WorktreeSelectorIssuer) Issue(principal *session.Principal, source session.SessionID, choice Worktree) string
Issue derives one opaque caller/source/current-choice selector.
func (*WorktreeSelectorIssuer) Match ¶ added in v0.0.24
func (i *WorktreeSelectorIssuer) Match(selector string, principal *session.Principal, source session.SessionID, current []Worktree) (Worktree, error)
Match checks every current candidate in constant time. It deliberately does not decode the selector or retain issued values.
Source Files
¶
- acp_placement.go
- attempts.go
- authn.go
- classification.go
- cleanup.go
- cors.go
- doc.go
- dreamreview.go
- dreamwire.go
- errorcodes.go
- errors.go
- event_recorder.go
- features.go
- grpc.go
- grpc_approveplan.go
- grpc_schedule.go
- grpc_team.go
- health.go
- http.go
- learnedskills.go
- learning.go
- mapper.go
- migration.go
- mutation_capability.go
- ownership.go
- placement.go
- placement_discovery.go
- placement_successor.go
- problem.go
- profile.go
- runid.go
- schedule.go
- schedule_manager.go
- serverinfo.go
- service.go
- session_mutation_inventory.go
- stale_maintenance.go
- storage_health.go
- storage_management.go
- team.go
- transcript.go
- watch.go
- worktree_selector.go