Files
pulse/pkg/server/telemetry_pulse_intelligence.go
T
rcourtman 3420fcdee4 Add schema v17 Patrol provider, cost, autonomy, and outcome telemetry
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.
2026-09-02 11:09:25 +01:00

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
}