mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
3420fcdee4
The 2026-09-01 Patrol assessment could not answer four questions from telemetry: what share of Patrol installs run local versus cloud models, what Patrol costs an install per month (issue 1789 estimates ~104k input tokens per full run at ~5.5 runs a day, above the Pro fee on non-Flash tiers), how the 240 of 255 fleet investigations that produced no plan actually ended, and which effective Patrol mode an install runs. Schema v17 adds closed buckets only. ai_provider_class classifies the Patrol model route (none, local, cloud_byok, cloud_subscription, hosted_quickstart, unknown) with a syntactic private-host check on custom OpenAI-compatible endpoints; it never resolves DNS or carries a provider, model, endpoint, or account. pulse_intelligence_patrol_autonomy_level is the effective level after licence and Autopilot gating. The two token fields bucket the existing local usage ledger's Patrol events, so exact counts and prices stay on the install. Thirteen investigation outcome counters partition the findings already counted as investigated, one bucket per finding, adding no finding, resource, or session identity. The strings are never omitted, so an empty value can only mean a pre-v17 sender; the receiver stores those as unknown. PRIVACY.md, its shipped mirror, the Settings preview interface, and the security-privacy and api-contracts subsystem notes carry the disclosure, and the coverage gap is registered in status.json. The agent-lifecycle and storage-recovery contracts name internal/api under their extension points, so both record that this telemetry is adjacent adoption analytics and never agent or storage state. The stable E2E tier pins the public schema version, so the disclosure spec moves to 17, and the Settings preview interface change carries a browser receipt from a scratch build of this tree at 1280x800 and 390x844. status.json also drops an identical duplicate of the ai-provider-guided-setup coverage-gap record that two merges each appended, which the private governance audit rejects. The bucket for a legacy config still pointing at the retired Pulse-hosted route is named hosted_legacy: the shipped privacy document discloses the vocabulary verbatim and the frontend copy contract keeps that document free of the retired hosted quickstart wording, so the telemetry package now pins the vocabulary at the source.
459 lines
21 KiB
Go
459 lines
21 KiB
Go
package server
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/agentcapabilities"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/telemetry"
|
|
)
|
|
|
|
func applyPulseIntelligenceTelemetrySnapshot(
|
|
snap *telemetry.Snapshot,
|
|
persistence *config.ConfigPersistence,
|
|
currentCfg *config.Config,
|
|
actionSnapshot telemetry.PulseIntelligenceActionSnapshot,
|
|
now time.Time,
|
|
) {
|
|
if snap == nil {
|
|
return
|
|
}
|
|
if now.IsZero() {
|
|
now = time.Now()
|
|
}
|
|
now = now.UTC()
|
|
since := now.Add(-telemetry.PulseIntelligenceTelemetryWindow)
|
|
|
|
// Schema v17 closed-vocabulary defaults. A missing usage ledger or AI
|
|
// service must still export a valid bucket, never an empty string.
|
|
if snap.AIProviderClass == "" {
|
|
snap.AIProviderClass = telemetry.AIProviderClassNone
|
|
}
|
|
snap.PulseIntelligencePatrolAutonomyLevel = telemetry.NormalizePatrolAutonomyLevelForTelemetry(actionSnapshot.PatrolAutonomyLevel)
|
|
snap.PulseIntelligencePatrolInputTokensBucket30d = telemetry.PatrolTokenBucketZero
|
|
snap.PulseIntelligencePatrolOutputTokensBucket30d = telemetry.PatrolTokenBucketZero
|
|
|
|
applyPulseIntelligenceAIUsageSnapshot(snap, persistence, since)
|
|
applyPulseIntelligencePatrolRunSnapshot(snap, persistence, since)
|
|
applyPulseIntelligenceFindingSnapshot(snap, persistence, since)
|
|
applyPulseIntelligenceExternalAgentSnapshot(snap, persistence, currentCfg, since, now)
|
|
applyPulseIntelligenceWorkflowPromptSnapshot(snap, persistence, since)
|
|
|
|
snap.PulseIntelligenceActionPlans30d = actionSnapshot.ActionPlans30d
|
|
snap.PulseIntelligenceApprovalRequests30d = actionSnapshot.ApprovalRequests30d
|
|
snap.PulseIntelligenceRejectedActionDecisions30d = actionSnapshot.RejectedActionDecisions30d
|
|
snap.PulseIntelligenceApprovedActionDecisions30d = actionSnapshot.ApprovedActionDecisions30d
|
|
snap.PulseIntelligenceApprovedActionAttempts30d = actionSnapshot.ApprovedActionAttempts30d
|
|
snap.PulseIntelligenceApprovedActionSuccesses30d = actionSnapshot.ApprovedActionSuccesses30d
|
|
snap.PulseIntelligencePatrolActionPlans30d = actionSnapshot.PatrolActionPlans30d
|
|
snap.PulseIntelligencePatrolApprovalRequests30d = actionSnapshot.PatrolApprovalRequests30d
|
|
snap.PulseIntelligencePatrolRejectedActionDecisions30d = actionSnapshot.PatrolRejectedActionDecisions30d
|
|
snap.PulseIntelligencePatrolApprovedActionDecisions30d = actionSnapshot.PatrolApprovedActionDecisions30d
|
|
snap.PulseIntelligencePatrolApprovedActionAttempts30d = actionSnapshot.PatrolApprovedActionAttempts30d
|
|
snap.PulseIntelligencePatrolApprovedActionSuccesses30d = actionSnapshot.PatrolApprovedActionSuccesses30d
|
|
snap.PulseIntelligenceApprovedActionFailuresPreDispatch30d = actionSnapshot.ApprovedActionFailuresPreDispatch30d
|
|
snap.PulseIntelligenceApprovedActionFailuresExecution30d = actionSnapshot.ApprovedActionFailuresExecution30d
|
|
snap.PulseIntelligenceApprovedActionFailuresUnverified30d = actionSnapshot.ApprovedActionFailuresUnverified30d
|
|
snap.PulseIntelligenceApprovedActionStuckExecuting30d = actionSnapshot.ApprovedActionStuckExecuting30d
|
|
snap.PulseIntelligenceApprovedActionInFlight30d = actionSnapshot.ApprovedActionInFlight30d
|
|
snap.PulseIntelligenceApprovedActionUnclassified30d = actionSnapshot.ApprovedActionUnclassified30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsPlanStale30d = actionSnapshot.ApprovedActionRefusalsPlanStale30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsPolicy30d = actionSnapshot.ApprovedActionRefusalsPolicy30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsCapability30d = actionSnapshot.ApprovedActionRefusalsCapability30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsTargetChanged30d = actionSnapshot.ApprovedActionRefusalsTargetChanged30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsPrerequisite30d = actionSnapshot.ApprovedActionRefusalsPrerequisite30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsContract30d = actionSnapshot.ApprovedActionRefusalsContract30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsUncoded30d = actionSnapshot.ApprovedActionRefusalsUncoded30d
|
|
snap.PulseIntelligenceApprovedActionRefusalsOther30d = actionSnapshot.ApprovedActionRefusalsOther30d
|
|
snap.PulseIntelligenceVerifiedFindingResolutions30d = actionSnapshot.VerifiedFindingResolutions30d
|
|
snap.PulseIntelligenceApprovedActionLastFailureReason30d = actionSnapshot.ApprovedActionLastFailureReason30d
|
|
snap.PulseIntelligencePatrolBlockedCause = actionSnapshot.PatrolBlockedCause
|
|
|
|
applyPulseIntelligenceAdoptionSnapshot(snap)
|
|
}
|
|
|
|
func applyPulseIntelligenceAdoptionSnapshot(snap *telemetry.Snapshot) {
|
|
if snap == nil {
|
|
return
|
|
}
|
|
|
|
snap.PulseIntelligenceLoopConfigured = snap.AIEnabled ||
|
|
snap.PatrolEnabled ||
|
|
snap.AIActionsEnabled ||
|
|
snap.PulseIntelligenceExternalAgentEnabled
|
|
|
|
snap.PulseIntelligenceGovernedActionActive30d =
|
|
snap.PulseIntelligenceActionPlans30d > 0 ||
|
|
snap.PulseIntelligenceApprovalRequests30d > 0 ||
|
|
snap.PulseIntelligenceRejectedActionDecisions30d > 0 ||
|
|
snap.PulseIntelligenceApprovedActionDecisions30d > 0 ||
|
|
snap.PulseIntelligenceApprovedActionAttempts30d > 0
|
|
|
|
patrolActive := snap.PulseIntelligencePatrolAICalls30d > 0 ||
|
|
snap.PulseIntelligencePatrolRuns30d > 0 ||
|
|
snap.PulseIntelligencePatrolNewFindings30d > 0 ||
|
|
snap.PulseIntelligencePatrolInvestigations30d > 0 ||
|
|
snap.PulseIntelligencePatrolResolvedFindings30d > 0
|
|
patrolIssueEvidenceCount := snap.PulseIntelligencePatrolNewFindings30d +
|
|
snap.PulseIntelligencePatrolInvestigations30d +
|
|
snap.PulseIntelligencePatrolResolvedFindings30d
|
|
patrolIssueEvidenceActive := patrolIssueEvidenceCount > 0
|
|
assistantCollaborationCount := snap.PulseIntelligenceAssistantContextAICalls30d +
|
|
snap.PulseIntelligenceAssistantToolCalls30d
|
|
assistantCollaborationActive := assistantCollaborationCount > 0
|
|
externalAgentCollaborationActive := pulseIntelligenceExternalAgentActivity(snap)
|
|
externalAgentCollaborationCount := 0
|
|
if externalAgentCollaborationActive {
|
|
externalAgentCollaborationCount = 1
|
|
}
|
|
mcpAdapterCollaborationActive := snap.PulseIntelligenceMCPAdapterUsed30d
|
|
contextualCollaborationCount := assistantCollaborationCount + externalAgentCollaborationCount
|
|
contextualCollaborationActive := contextualCollaborationCount > 0
|
|
governedActionDecisionActive := snap.PulseIntelligenceRejectedActionDecisions30d > 0 ||
|
|
snap.PulseIntelligenceApprovedActionDecisions30d > 0 ||
|
|
snap.PulseIntelligenceApprovedActionAttempts30d > 0
|
|
approvedExecutionActive := snap.PulseIntelligenceApprovedActionAttempts30d > 0
|
|
approvedSuccessActive := snap.PulseIntelligenceApprovedActionSuccesses30d > 0
|
|
verifiedFindingResolutionActive := snap.PulseIntelligenceVerifiedFindingResolutions30d > 0
|
|
patrolControlProof := telemetry.ClassifyPulseIntelligencePatrolControlProof(telemetry.PulseIntelligencePatrolControlProofInput{
|
|
PatrolControlStarterCount: snap.PulseIntelligencePatrolControlOperationsLoopStarterRequests30d,
|
|
PatrolIssueEvidenceCount: patrolIssueEvidenceCount,
|
|
ContextualCollaborationCount: contextualCollaborationCount,
|
|
ApprovedDecisionCount: snap.PulseIntelligenceApprovedActionDecisions30d,
|
|
RejectedDecisionCount: snap.PulseIntelligenceRejectedActionDecisions30d,
|
|
VerifiedOutcomeCount: snap.PulseIntelligenceVerifiedFindingResolutions30d,
|
|
})
|
|
snap.PulseIntelligenceCompleteOperationsLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
contextualCollaborationActive &&
|
|
governedActionDecisionActive
|
|
snap.PulseIntelligenceApprovedExecutionLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
contextualCollaborationActive &&
|
|
approvedExecutionActive
|
|
snap.PulseIntelligenceResolvedOperationsLoop30d =
|
|
verifiedFindingResolutionActive
|
|
snap.PulseIntelligencePatrolControlCompletedOperationsLoop30d = patrolControlProof.Completed
|
|
snap.PulseIntelligencePatrolControlResolvedOperationsLoop30d = patrolControlProof.Resolved
|
|
snap.PulseIntelligencePatrolControlPaidCompletedOperationsLoop30d =
|
|
snap.PaidLicense && snap.PulseIntelligencePatrolControlCompletedOperationsLoop30d
|
|
snap.PulseIntelligencePatrolControlPaidResolvedOperationsLoop30d =
|
|
snap.PaidLicense && snap.PulseIntelligencePatrolControlResolvedOperationsLoop30d
|
|
// Legacy Pro activation cohort fields mirror Patrol control for continuity.
|
|
snap.PulseIntelligenceProActivationCompletedOperationsLoop30d = snap.PulseIntelligencePatrolControlCompletedOperationsLoop30d
|
|
snap.PulseIntelligenceProActivationResolvedOperationsLoop30d = snap.PulseIntelligencePatrolControlResolvedOperationsLoop30d
|
|
snap.PulseIntelligenceProActivationPaidCompletedOperationsLoop30d =
|
|
snap.PulseIntelligencePatrolControlPaidCompletedOperationsLoop30d
|
|
snap.PulseIntelligenceProActivationPaidResolvedOperationsLoop30d =
|
|
snap.PulseIntelligencePatrolControlPaidResolvedOperationsLoop30d
|
|
snap.PulseIntelligenceAssistantOperationsLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
assistantCollaborationActive &&
|
|
governedActionDecisionActive
|
|
snap.PulseIntelligenceAssistantApprovedExecutionLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
assistantCollaborationActive &&
|
|
approvedExecutionActive
|
|
snap.PulseIntelligenceAssistantApprovedActionSuccessLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
assistantCollaborationActive &&
|
|
approvedSuccessActive
|
|
snap.PulseIntelligenceAssistantResolvedOperationsLoop30d =
|
|
verifiedFindingResolutionActive &&
|
|
assistantCollaborationActive
|
|
snap.PulseIntelligenceExternalAgentOperationsLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
externalAgentCollaborationActive &&
|
|
governedActionDecisionActive
|
|
snap.PulseIntelligenceExternalAgentApprovedExecutionLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
externalAgentCollaborationActive &&
|
|
approvedExecutionActive
|
|
snap.PulseIntelligenceExternalAgentApprovedActionSuccessLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
externalAgentCollaborationActive &&
|
|
approvedSuccessActive
|
|
snap.PulseIntelligenceExternalAgentResolvedOperationsLoop30d =
|
|
verifiedFindingResolutionActive &&
|
|
externalAgentCollaborationActive
|
|
snap.PulseIntelligenceMCPAdapterOperationsLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
mcpAdapterCollaborationActive &&
|
|
governedActionDecisionActive
|
|
snap.PulseIntelligenceMCPAdapterApprovedExecutionLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
mcpAdapterCollaborationActive &&
|
|
approvedExecutionActive
|
|
snap.PulseIntelligenceMCPAdapterApprovedActionSuccessLoop30d =
|
|
patrolIssueEvidenceActive &&
|
|
mcpAdapterCollaborationActive &&
|
|
approvedSuccessActive
|
|
snap.PulseIntelligenceMCPAdapterResolvedOperationsLoop30d =
|
|
verifiedFindingResolutionActive &&
|
|
mcpAdapterCollaborationActive
|
|
|
|
snap.PulseIntelligenceLoopActive30d =
|
|
snap.PulseIntelligenceAssistantAICalls30d > 0 ||
|
|
snap.PulseIntelligenceOperationsLoopStarterRequests30d > 0 ||
|
|
patrolActive ||
|
|
pulseIntelligenceExternalAgentActivity(snap) ||
|
|
snap.PulseIntelligenceGovernedActionActive30d
|
|
}
|
|
|
|
func pulseIntelligenceExternalAgentActivity(snap *telemetry.Snapshot) bool {
|
|
if snap == nil {
|
|
return false
|
|
}
|
|
evidence := telemetry.PulseIntelligenceExternalAgentEvidence{
|
|
Used: snap.PulseIntelligenceExternalAgentUsed30d,
|
|
MCPAdapterUsed: snap.PulseIntelligenceMCPAdapterUsed30d,
|
|
ContextRequests: snap.PulseIntelligenceExternalAgentContextRequests30d,
|
|
EventStreamRequests: snap.PulseIntelligenceExternalAgentEventStreamRequests30d,
|
|
ProvisioningRequests: snap.PulseIntelligenceExternalAgentProvisioningRequests30d,
|
|
OperatorStateRequests: snap.PulseIntelligenceExternalAgentOperatorStateRequests30d,
|
|
FindingRequests: snap.PulseIntelligenceExternalAgentFindingRequests30d,
|
|
ActionRequests: snap.PulseIntelligenceExternalAgentActionRequests30d,
|
|
}
|
|
return evidence.CollaborationActive()
|
|
}
|
|
|
|
func applyPulseIntelligenceAIUsageSnapshot(snap *telemetry.Snapshot, persistence *config.ConfigPersistence, since time.Time) {
|
|
if snap == nil || persistence == nil {
|
|
return
|
|
}
|
|
history, err := persistence.LoadAIUsageHistory()
|
|
if err != nil || history == nil {
|
|
return
|
|
}
|
|
evidence := telemetry.PulseIntelligenceAIUsageEvidenceFromHistory(history, since)
|
|
snap.PulseIntelligenceAssistantAICalls30d = evidence.AssistantAICalls
|
|
snap.PulseIntelligenceAssistantContextAICalls30d = evidence.AssistantContextAICalls
|
|
snap.PulseIntelligenceAssistantToolCalls30d = evidence.AssistantToolCalls
|
|
snap.PulseIntelligencePatrolAICalls30d = evidence.PatrolAICalls
|
|
// Exact token totals stay local; only the closed bucket is exported.
|
|
snap.PulseIntelligencePatrolInputTokensBucket30d = telemetry.PatrolInputTokensBucket(evidence.PatrolInputTokens)
|
|
snap.PulseIntelligencePatrolOutputTokensBucket30d = telemetry.PatrolOutputTokensBucket(evidence.PatrolOutputTokens)
|
|
}
|
|
|
|
func applyPulseIntelligencePatrolRunSnapshot(snap *telemetry.Snapshot, persistence *config.ConfigPersistence, since time.Time) {
|
|
if snap == nil || persistence == nil {
|
|
return
|
|
}
|
|
history, err := persistence.LoadPatrolRunHistory()
|
|
if err != nil || history == nil {
|
|
return
|
|
}
|
|
// Run volume comes from the uncapped daily tally. Counting history.Runs
|
|
// directly saturates at the operator-facing history cap, which on an install
|
|
// patrolling on a normal schedule is reached within hours of a thirty-day
|
|
// window rather than at its end.
|
|
snap.PulseIntelligencePatrolRuns30d += history.PatrolRunsSince(since)
|
|
for _, run := range history.Runs {
|
|
observedAt := run.CompletedAt
|
|
if observedAt.IsZero() {
|
|
observedAt = run.StartedAt
|
|
}
|
|
if observedAt.IsZero() || observedAt.Before(since) {
|
|
continue
|
|
}
|
|
if run.NewFindings > 0 {
|
|
snap.PulseIntelligencePatrolNewFindings30d += run.NewFindings
|
|
}
|
|
}
|
|
}
|
|
|
|
func applyPulseIntelligenceFindingSnapshot(snap *telemetry.Snapshot, persistence *config.ConfigPersistence, since time.Time) {
|
|
if snap == nil || persistence == nil {
|
|
return
|
|
}
|
|
data, err := persistence.LoadAIFindings()
|
|
if err != nil || data == nil {
|
|
return
|
|
}
|
|
var outcomes telemetry.PatrolInvestigationOutcomeCounts
|
|
for _, finding := range data.Findings {
|
|
if pulseIntelligenceFindingInvestigatedSince(finding, since) {
|
|
snap.PulseIntelligencePatrolInvestigations30d++
|
|
// Partition the same population by its current investigation
|
|
// state so the outcome buckets always sum to the investigation
|
|
// count. Only the closed bucket leaves the install.
|
|
outcomes.Add(finding.InvestigationOutcome, finding.InvestigationStatus)
|
|
}
|
|
if pulseIntelligenceFindingResolvedSince(finding, since) {
|
|
snap.PulseIntelligencePatrolResolvedFindings30d++
|
|
}
|
|
}
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixVerified30d = outcomes.FixVerified
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixQueued30d = outcomes.FixQueued
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixExecuted30d = outcomes.FixExecuted
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixRejected30d = outcomes.FixRejected
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixFailed30d = outcomes.FixFailed
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFixVerificationUnknown30d = outcomes.FixVerificationUnknown
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeResolved30d = outcomes.Resolved
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeNeedsAttention30d = outcomes.NeedsAttention
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeCannotFix30d = outcomes.CannotFix
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeTimedOut30d = outcomes.TimedOut
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeInProgress30d = outcomes.InProgress
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeFailed30d = outcomes.Failed
|
|
snap.PulseIntelligencePatrolInvestigationOutcomeOther30d = outcomes.Other
|
|
}
|
|
|
|
func pulseIntelligenceFindingInvestigatedSince(finding *config.AIFindingRecord, since time.Time) bool {
|
|
if finding == nil {
|
|
return false
|
|
}
|
|
if finding.LastInvestigatedAt != nil && !finding.LastInvestigatedAt.IsZero() && !finding.LastInvestigatedAt.Before(since) {
|
|
return true
|
|
}
|
|
record := finding.InvestigationRecord
|
|
if record == nil {
|
|
return false
|
|
}
|
|
if !record.StartedAt.IsZero() && !record.StartedAt.Before(since) {
|
|
return true
|
|
}
|
|
return record.CompletedAt != nil && !record.CompletedAt.IsZero() && !record.CompletedAt.Before(since)
|
|
}
|
|
|
|
func pulseIntelligenceFindingResolvedSince(finding *config.AIFindingRecord, since time.Time) bool {
|
|
if finding == nil {
|
|
return false
|
|
}
|
|
if finding.ResolvedAt != nil && !finding.ResolvedAt.IsZero() && !finding.ResolvedAt.Before(since) {
|
|
return true
|
|
}
|
|
switch strings.TrimSpace(finding.InvestigationOutcome) {
|
|
case "resolved", "fix_verified":
|
|
default:
|
|
return false
|
|
}
|
|
if finding.LastInvestigatedAt != nil && !finding.LastInvestigatedAt.IsZero() && !finding.LastInvestigatedAt.Before(since) {
|
|
return true
|
|
}
|
|
record := finding.InvestigationRecord
|
|
return record != nil && record.CompletedAt != nil && !record.CompletedAt.IsZero() && !record.CompletedAt.Before(since)
|
|
}
|
|
|
|
func applyPulseIntelligenceExternalAgentSnapshot(
|
|
snap *telemetry.Snapshot,
|
|
persistence *config.ConfigPersistence,
|
|
currentCfg *config.Config,
|
|
since time.Time,
|
|
now time.Time,
|
|
) {
|
|
if snap == nil {
|
|
return
|
|
}
|
|
if currentCfg != nil {
|
|
surfaceScopes := pulseIntelligenceExternalAgentSurfaceScopes(agentcapabilities.CanonicalManifest())
|
|
if len(surfaceScopes) > 0 {
|
|
for _, token := range currentCfg.APITokens {
|
|
if pulseIntelligenceExternalAgentToken(token, surfaceScopes, now) {
|
|
snap.PulseIntelligenceExternalAgentEnabled = true
|
|
}
|
|
if pulseIntelligenceExternalAgentOperationsLoopToken(token, surfaceScopes, now) {
|
|
snap.PulseIntelligenceExternalAgentOperationsLoopReady = true
|
|
}
|
|
if snap.PulseIntelligenceExternalAgentEnabled && snap.PulseIntelligenceExternalAgentOperationsLoopReady {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if persistence == nil {
|
|
return
|
|
}
|
|
history, err := persistence.LoadExternalAgentActivityHistory()
|
|
if err != nil || history == nil {
|
|
return
|
|
}
|
|
evidence := telemetry.PulseIntelligenceExternalAgentEvidenceFromHistory(history, since)
|
|
snap.PulseIntelligenceExternalAgentUsed30d = evidence.Used
|
|
snap.PulseIntelligenceMCPAdapterUsed30d = evidence.MCPAdapterUsed
|
|
snap.PulseIntelligenceExternalAgentContextRequests30d = evidence.ContextRequests
|
|
snap.PulseIntelligenceExternalAgentEventStreamRequests30d = evidence.EventStreamRequests
|
|
snap.PulseIntelligenceExternalAgentProvisioningRequests30d = evidence.ProvisioningRequests
|
|
snap.PulseIntelligenceExternalAgentOperatorStateRequests30d = evidence.OperatorStateRequests
|
|
snap.PulseIntelligenceExternalAgentFindingRequests30d = evidence.FindingRequests
|
|
snap.PulseIntelligenceExternalAgentActionRequests30d = evidence.ActionRequests
|
|
}
|
|
|
|
func applyPulseIntelligenceWorkflowPromptSnapshot(snap *telemetry.Snapshot, persistence *config.ConfigPersistence, since time.Time) {
|
|
if snap == nil || persistence == nil {
|
|
return
|
|
}
|
|
history, err := persistence.LoadWorkflowPromptActivityHistory()
|
|
if err != nil || history == nil {
|
|
return
|
|
}
|
|
for _, event := range history.Events {
|
|
if event.Timestamp.IsZero() || event.Timestamp.Before(since) {
|
|
continue
|
|
}
|
|
if strings.TrimSpace(event.PromptName) != agentcapabilities.PulseWorkflowPromptOperationsLoop {
|
|
continue
|
|
}
|
|
snap.PulseIntelligenceOperationsLoopStarterRequests30d++
|
|
switch strings.TrimSpace(event.Surface) {
|
|
case config.WorkflowPromptActivitySurfacePulseAssistant:
|
|
snap.PulseIntelligenceAssistantOperationsLoopStarterRequests30d++
|
|
case config.WorkflowPromptActivitySurfacePulsePatrol:
|
|
snap.PulseIntelligencePatrolOperationsLoopStarterRequests30d++
|
|
snap.PulseIntelligencePatrolControlOperationsLoopStarterRequests30d++
|
|
case config.WorkflowPromptActivitySurfacePatrolControl:
|
|
snap.PulseIntelligencePatrolControlOperationsLoopStarterRequests30d++
|
|
case config.WorkflowPromptActivitySurfacePatrolAutonomy:
|
|
snap.PulseIntelligencePatrolControlOperationsLoopStarterRequests30d++
|
|
// This telemetry field is the compatibility aggregate for paid Patrol autonomy handoffs.
|
|
snap.PulseIntelligenceProActivationOperationsLoopStarterRequests30d++
|
|
case config.WorkflowPromptActivitySurfaceProActivation:
|
|
snap.PulseIntelligencePatrolControlOperationsLoopStarterRequests30d++
|
|
snap.PulseIntelligenceProActivationOperationsLoopStarterRequests30d++
|
|
case config.WorkflowPromptActivitySurfacePulseMCP:
|
|
snap.PulseIntelligenceMCPOperationsLoopStarterRequests30d++
|
|
}
|
|
}
|
|
}
|
|
|
|
func pulseIntelligenceExternalAgentSurfaceScopes(manifest agentcapabilities.Manifest) []string {
|
|
return agentcapabilities.RequiredCapabilityScopes(
|
|
agentcapabilities.ManifestSurfaceToolCapabilities(manifest, agentcapabilities.SurfaceIDPulseMCP),
|
|
)
|
|
}
|
|
|
|
func pulseIntelligenceExternalAgentToken(token config.APITokenRecord, surfaceScopes []string, now time.Time) bool {
|
|
if token.ExpiresAt != nil && now.After(token.ExpiresAt.UTC()) {
|
|
return false
|
|
}
|
|
token = token.Clone()
|
|
for _, scope := range surfaceScopes {
|
|
if strings.TrimSpace(scope) == "" {
|
|
continue
|
|
}
|
|
if token.HasScope(scope) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func pulseIntelligenceExternalAgentOperationsLoopToken(token config.APITokenRecord, surfaceScopes []string, now time.Time) bool {
|
|
if token.ExpiresAt != nil && now.After(token.ExpiresAt.UTC()) {
|
|
return false
|
|
}
|
|
token = token.Clone()
|
|
coversLoop := false
|
|
for _, scope := range surfaceScopes {
|
|
scope = strings.TrimSpace(scope)
|
|
if scope == "" {
|
|
continue
|
|
}
|
|
coversLoop = true
|
|
if !token.HasScope(scope) {
|
|
return false
|
|
}
|
|
}
|
|
return coversLoop
|
|
}
|