mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-21 19:25:57 +00:00
3ffd622de0
Production telemetry showed 1,437 installs on the external-agent surface but only 2 on the Pulse MCP adapter. Live verification proved the pulse_mcp telemetry surface attribution is honest (adapter tools/call records surface=pulse_mcp; header-less calls record agent_api), so the gap is real - but the funnel comparison is also misleading: agent_api counts any scoped API-token REST consumer (scripts, dashboards polling /api/config/nodes or /api/ai/patrol/findings), not BYO AI agents. Recorded that interpretation constraint on PulseIntelligenceExternalAgentEvidence. The genuine product gap: the in-app connector setup (Settings -> Pulse Intelligence -> Assistant -> External agents) was invisible to sidebar search. Searching 'mcp', 'claude', 'opencode', or 'connector' returned 'No settings found' because search matched only nav labels and header descriptions. - Add search-only keywords to SettingsNavItem and match them in filteredTabGroups; the Assistant item now carries mcp / model context protocol / external agent(s) / claude / opencode / connector / pulse-mcp. - Name external agent (MCP) connectors in the Assistant header description (EN/DE/ES kept in sync), so the page header states what the page contains. - Pin the search behavior with parameterized useSettingsAccess tests plus a settingsArchitecture source proof; record the keyword channel in the frontend-primitives contract (obligation 41) and the product-copy allowance in the security-privacy i18n extension point.
162 lines
5.4 KiB
Go
162 lines
5.4 KiB
Go
package telemetry
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
// PulseIntelligenceAIUsageEvidence is the content-free subset of AI usage
|
|
// history used to prove Pulse Intelligence loop collaboration.
|
|
type PulseIntelligenceAIUsageEvidence struct {
|
|
AssistantAICalls int
|
|
AssistantContextAICalls int
|
|
AssistantToolCalls int
|
|
PatrolAICalls int
|
|
}
|
|
|
|
// PulseIntelligenceAIUsageEvidenceFromHistory projects AI usage history into
|
|
// the same content-free Pulse Intelligence evidence used by telemetry and the
|
|
// agent operations-loop status endpoint.
|
|
func PulseIntelligenceAIUsageEvidenceFromHistory(history *config.AIUsageHistoryData, since time.Time) PulseIntelligenceAIUsageEvidence {
|
|
var evidence PulseIntelligenceAIUsageEvidence
|
|
if history == nil {
|
|
return evidence
|
|
}
|
|
for _, event := range history.Events {
|
|
if event.Timestamp.IsZero() || event.Timestamp.Before(since) {
|
|
continue
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(event.UseCase)) {
|
|
case "chat":
|
|
evidence.AssistantAICalls++
|
|
if PulseIntelligenceAssistantContextEvent(event) {
|
|
evidence.AssistantContextAICalls++
|
|
}
|
|
if event.ToolCallCount > 0 {
|
|
evidence.AssistantToolCalls += event.ToolCallCount
|
|
}
|
|
case "patrol":
|
|
evidence.PatrolAICalls++
|
|
}
|
|
}
|
|
return evidence
|
|
}
|
|
|
|
// PulseIntelligenceAssistantContextEvent reports whether a chat usage event
|
|
// carried governed context. It deliberately ignores prompt text and responses.
|
|
func PulseIntelligenceAssistantContextEvent(event config.AIUsageEventRecord) bool {
|
|
return strings.TrimSpace(event.ContextScope) != "" ||
|
|
strings.TrimSpace(event.TargetType) != "" ||
|
|
strings.TrimSpace(event.TargetID) != "" ||
|
|
strings.TrimSpace(event.FindingID) != ""
|
|
}
|
|
|
|
// PulseIntelligenceExternalAgentEvidence is the content-free subset of
|
|
// authenticated agent/MCP route activity used to prove external collaboration.
|
|
//
|
|
// Interpretation constraint: the agent_api surface counts any suitably scoped
|
|
// API-token request to manifest-listed routes — including plain REST consumers
|
|
// like scripts and dashboards hitting GET /api/config/nodes or
|
|
// /api/ai/patrol/findings — so Used does NOT imply a BYO AI agent. Only
|
|
// MCPAdapterUsed (the pulse_mcp surface header) certainly identifies an MCP
|
|
// client. Do not read Used-vs-MCPAdapterUsed as an adoption funnel of the
|
|
// same population.
|
|
type PulseIntelligenceExternalAgentEvidence struct {
|
|
Used bool
|
|
MCPAdapterUsed bool
|
|
ContextRequests int
|
|
EventStreamRequests int
|
|
ProvisioningRequests int
|
|
OperatorStateRequests int
|
|
FindingRequests int
|
|
ActionRequests int
|
|
}
|
|
|
|
// CollaborationActive reports whether an external agent or MCP adapter used a
|
|
// manifest-published Pulse Intelligence capability in the evidence window.
|
|
func (e PulseIntelligenceExternalAgentEvidence) CollaborationActive() bool {
|
|
return e.Used ||
|
|
e.MCPAdapterUsed ||
|
|
e.ContextRequests > 0 ||
|
|
e.EventStreamRequests > 0 ||
|
|
e.ProvisioningRequests > 0 ||
|
|
e.OperatorStateRequests > 0 ||
|
|
e.FindingRequests > 0 ||
|
|
e.ActionRequests > 0
|
|
}
|
|
|
|
// CollaborationCount returns a coarse activity count for step-level rollups.
|
|
func (e PulseIntelligenceExternalAgentEvidence) CollaborationCount() int {
|
|
count := e.ContextRequests +
|
|
e.EventStreamRequests +
|
|
e.ProvisioningRequests +
|
|
e.OperatorStateRequests +
|
|
e.FindingRequests +
|
|
e.ActionRequests
|
|
if count > 0 {
|
|
return count
|
|
}
|
|
if e.Used || e.MCPAdapterUsed {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// PulseIntelligenceExternalAgentEvidenceFromHistory projects external-agent
|
|
// activity history into content-free collaboration evidence.
|
|
func PulseIntelligenceExternalAgentEvidenceFromHistory(history *config.ExternalAgentActivityHistoryData, since time.Time) PulseIntelligenceExternalAgentEvidence {
|
|
var evidence PulseIntelligenceExternalAgentEvidence
|
|
if history == nil {
|
|
return evidence
|
|
}
|
|
for _, event := range history.Events {
|
|
if event.Timestamp.IsZero() || event.Timestamp.Before(since) {
|
|
continue
|
|
}
|
|
if !PulseIntelligenceExternalAgentActivitySurface(event.Surface) {
|
|
continue
|
|
}
|
|
evidence.Used = true
|
|
if strings.TrimSpace(event.Surface) == config.ExternalAgentActivitySurfacePulseMCP {
|
|
evidence.MCPAdapterUsed = true
|
|
}
|
|
evidence.ApplyActivity(event.Activity)
|
|
}
|
|
return evidence
|
|
}
|
|
|
|
// PulseIntelligenceExternalAgentActivitySurface reports whether a persisted
|
|
// activity surface is part of the governed external-agent contract.
|
|
func PulseIntelligenceExternalAgentActivitySurface(surface string) bool {
|
|
switch strings.TrimSpace(surface) {
|
|
case config.ExternalAgentActivitySurfaceAgentAPI, config.ExternalAgentActivitySurfacePulseMCP:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// ApplyActivity increments the matching content-free external-agent activity
|
|
// bucket.
|
|
func (e *PulseIntelligenceExternalAgentEvidence) ApplyActivity(activity string) {
|
|
if e == nil {
|
|
return
|
|
}
|
|
switch strings.TrimSpace(activity) {
|
|
case config.ExternalAgentActivityResourceContext, config.ExternalAgentActivityFleetContext:
|
|
e.ContextRequests++
|
|
case config.ExternalAgentActivityEventStream:
|
|
e.EventStreamRequests++
|
|
case config.ExternalAgentActivityProvisioning:
|
|
e.ProvisioningRequests++
|
|
case config.ExternalAgentActivityOperatorState:
|
|
e.OperatorStateRequests++
|
|
case config.ExternalAgentActivityFindingList, config.ExternalAgentActivityFindingDecision:
|
|
e.FindingRequests++
|
|
case config.ExternalAgentActivityActionPlan, config.ExternalAgentActivityActionDecision, config.ExternalAgentActivityActionExecute:
|
|
e.ActionRequests++
|
|
}
|
|
}
|