package extension import ( "context" "time" core "agent-system-core" ) // WorkObservation is built by the host from a committed message or an actual // retrieval, tool, or visible analysis result. Callers and models cannot supply // Actor, ProjectID, source identity, or the source authorization epoch. // RunID may be empty for a newly committed user message or chat command. type WorkObservation struct { EventID string `json:"eventId"` Phase string `json:"phase"` Actor string `json:"actor"` Owner string `json:"owner"` ProjectID core.ID `json:"projectId"` ConversationID core.ID `json:"conversationId,omitempty"` RunID core.ID `json:"runId,omitempty"` SourceKind string `json:"sourceKind"` SourceID string `json:"sourceId"` SourceVersion string `json:"sourceVersion"` SourceSHA256 string `json:"sourceSha256"` PartIndex int `json:"partIndex"` SpanStart int `json:"spanStart"` // UTF-8 byte offset in the exact source SpanEnd int `json:"spanEnd"` Text string `json:"text,omitempty"` // only the actual authorized fragment Scope string `json:"scope"` // private|project|controlled Delivery string `json:"delivery"` // complete|summary-only|partial DeliveryReason string `json:"deliveryReason,omitempty"` Metadata map[string]string `json:"metadata,omitempty"` AuthorizationEpoch string `json:"authorizationEpoch"` ObservedAt time.Time `json:"observedAt"` } type WorkObservationState string const ( WorkObservationAccepted WorkObservationState = "accepted" // durable intake, analysis may be pending WorkObservationPending WorkObservationState = "pending" WorkObservationDenied WorkObservationState = "denied" WorkObservationPartial WorkObservationState = "partial" ) type WorkObservationResult struct { ReceiptID string `json:"receiptId"` State WorkObservationState `json:"state"` Reason string `json:"reason,omitempty"` } // ProjectWorkSource is one exact part consumed by the pending action. A hash // mismatch or missing part must block Preflight rather than use a stale receipt. type ProjectWorkSource struct { EventID string `json:"eventId"` SourceKind string `json:"sourceKind"` SourceID string `json:"sourceId"` SourceVersion string `json:"sourceVersion"` SourceSHA256 string `json:"sourceSha256"` PartIndex int `json:"partIndex"` } // ProjectWorkAction is constructed by the host immediately before model use, // project command dispatch, or another project-affecting action. ProjectID is // checked against the stored conversation or run, never trusted from Params. type ProjectWorkAction struct { Actor string `json:"actor"` Owner string `json:"owner"` ProjectID core.ID `json:"projectId"` ConversationID core.ID `json:"conversationId,omitempty"` RunID core.ID `json:"runId,omitempty"` Action string `json:"action"` TargetID string `json:"targetId,omitempty"` TargetVersion string `json:"targetVersion,omitempty"` Manifest []ProjectWorkSource `json:"manifest"` SourceWatermark string `json:"sourceWatermark"` } type ProjectWorkDecisionState string const ( ProjectWorkProceed ProjectWorkDecisionState = "proceed" ProjectWorkClarify ProjectWorkDecisionState = "clarify" ProjectWorkPending ProjectWorkDecisionState = "pending" ProjectWorkDenied ProjectWorkDecisionState = "denied" ) // ProjectWorkDecision is a domain decision, not a replacement for a controlled // document's separate one-time human approval. A successful Observe ACK cannot // be interpreted as ProjectWorkProceed. type ProjectWorkDecision struct { State ProjectWorkDecisionState `json:"state"` DecisionID string `json:"decisionId"` RiskID string `json:"riskId,omitempty"` RiskRevision string `json:"riskRevision,omitempty"` SourceWatermark string `json:"sourceWatermark,omitempty"` Reason string `json:"reason,omitempty"` Question string `json:"question,omitempty"` Options []string `json:"options,omitempty"` } // ProjectRiskAnswer comes from a current authenticated session and persisted // risk card. An answer is applied only after the provider commits a CAS receipt. type ProjectRiskAnswer struct { Actor string `json:"actor"` Owner string `json:"owner"` ProjectID core.ID `json:"projectId"` RiskID string `json:"riskId"` RiskRevision string `json:"riskRevision"` Answer string `json:"answer"` IdempotencyKey string `json:"idempotencyKey"` } // ProjectWorkObserver is owned by the project domain. Backend must check a // returned decision against the exact current source manifest and must fail // closed on missing provider, timeout, or partial analysis when project facts // are required. RDM remains the sole persistent risk-state owner. type ProjectWorkObserver struct { Observe func(context.Context, WorkObservation) (WorkObservationResult, error) Preflight func(context.Context, ProjectWorkAction) (ProjectWorkDecision, error) Respond func(context.Context, ProjectRiskAnswer) (ProjectWorkDecision, error) } // ProjectWorkObservationRegistry is optional so older in-process extensions do // not acquire a mandatory Registry method. Declarative plugins cannot register // a callback. The host accepts one trusted RDM provider. type ProjectWorkObservationRegistry interface { RegisterProjectWorkObserver(ProjectWorkObserver) error }