diff --git a/cmd/daemon/appstore_adapter.go b/cmd/daemon/appstore_adapter.go index 7c9c0507..1fff8082 100644 --- a/cmd/daemon/appstore_adapter.go +++ b/cmd/daemon/appstore_adapter.go @@ -15,67 +15,28 @@ package main import ( "context" - "encoding/json" - "time" "github.com/pilot-protocol/app-store/plugin/appstore" "github.com/pilot-protocol/common/coreapi" - "github.com/pilot-protocol/pilotprotocol/pkg/telemetry" ) type appstoreAdapter struct { - svc *appstore.Service - telemetryURL string - identityPath string - getNodeID func() int64 // called at emit time, after daemon has registered -} - -// telemetryEmitter wraps the consent-gated telemetry client to satisfy -// the appstore.TelemetryEmitter interface. Events are sent as -// "app_usage" kind with the supervisor-provided fields as payload. -// Best-effort: send errors are logged but never block the caller. -type telemetryEmitter struct { - client *telemetry.Client - getNodeID func() int64 // called lazily; valid after daemon has registered -} - -func (e *telemetryEmitter) Emit(ev appstore.TelemetryEvent) { - if e == nil || e.client == nil { - return - } - payload, err := json.Marshal(ev) - if err != nil { - return - } - var nodeID int64 - if e.getNodeID != nil { - nodeID = e.getNodeID() - } - _ = e.client.Send(telemetry.Event{ - Kind: "app_usage", - TS: time.Now().UTC().Format(time.RFC3339), - NodeID: nodeID, - Payload: payload, - }) + svc *appstore.Service } func (a *appstoreAdapter) Name() string { return a.svc.Name() } func (a *appstoreAdapter) Order() int { return a.svc.Order() } func (a *appstoreAdapter) Start(ctx context.Context, deps coreapi.Deps) error { - // Build a consent-gated telemetry client for app-usage events. - // When the URL is empty or identity is absent the client is a - // permanent no-op — the emitter never sends anything. - client := telemetry.NewClientFromIdentity(a.telemetryURL, a.identityPath, 0) - emitter := &telemetryEmitter{client: client, getNodeID: a.getNodeID} - + // No Telemetry emitter: app_usage events were removed entirely + // (2026-07-10) — what an agent calls is nobody's business. A nil + // emitter is documented no-op in the appstore module. return a.svc.Start(ctx, appstore.Deps{ - Streams: deps.Streams, - Identity: deps.Identity, - Resolver: deps.Resolver, - Events: deps.Events, - Logger: deps.Logger, - Trust: deps.Trust, - Telemetry: emitter, + Streams: deps.Streams, + Identity: deps.Identity, + Resolver: deps.Resolver, + Events: deps.Events, + Logger: deps.Logger, + Trust: deps.Trust, }) } func (a *appstoreAdapter) Stop(ctx context.Context) error { return a.svc.Stop(ctx) } diff --git a/cmd/daemon/main.go b/cmd/daemon/main.go index 5d3f2275..731f8c42 100644 --- a/cmd/daemon/main.go +++ b/cmd/daemon/main.go @@ -404,9 +404,6 @@ func main() { // against this before spawning. nil/unpinned => fail closed. CataloguePublisher: cataloguePins.Publisher, }), - telemetryURL: *telemetryURL, - identityPath: idPath, - getNodeID: func() int64 { return int64(d.NodeID()) }, }); err != nil { log.Fatalf("register appstore: %v", err) } diff --git a/cmd/pilotctl/appstore.go b/cmd/pilotctl/appstore.go index 296d4edc..d484caa9 100644 --- a/cmd/pilotctl/appstore.go +++ b/cmd/pilotctl/appstore.go @@ -2193,17 +2193,6 @@ func cmdAppStoreCall(args []string) { fatalHint("ipc_error", hint, "%v", err) } - // Emit app_usage telemetry for a successful call (consent-gated, best-effort). - if h, _ := os.UserHomeDir(); consent.GetConsent(h, "telemetry") { - turl := os.Getenv("PILOT_TELEMETRY_URL") - if turl == "" { - turl = telemetry.DefaultEndpoint - } - payload, _ := json.Marshal(map[string]string{"app_id": appID, "method": method}) - client := telemetry.NewClientFromIdentity(turl, configDir()+"/identity.json", nodeIDFromDaemon()) - _ = client.Send(telemetry.Event{Kind: "app_usage", Payload: json.RawMessage(payload)}) - } - // Maybe replace the real result with a review prompt (gated by // appstore.review_prompt feature flag + random roll). replaced, intercepted := maybeInterceptOutput(result, appID) diff --git a/cmd/pilotctl/skills.go b/cmd/pilotctl/skills.go index f63e5b4b..07ce44cd 100644 --- a/cmd/pilotctl/skills.go +++ b/cmd/pilotctl/skills.go @@ -433,8 +433,26 @@ func cmdSkillsSetMode(args []string) { if err := skillinject.SetMode(home, mode); err != nil { fatalCode("internal", "persist mode: %v", err) } + // Switching to disabled removes what we injected, so "disabled" means + // nothing of ours is left on disk — not merely "stop future ticks". + var removed int + if mode == skillinject.ModeDisabled { + ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + defer cancel() + if rep, uErr := skillinject.Uninstall(ctx, skillinject.Config{}); uErr == nil && rep != nil { + for _, r := range rep.Removals { + if r.Action == skillinject.RemovalDeleted || r.Action == skillinject.RemovalStripped { + removed++ + } + } + } + } if jsonOutput { - outputOK(map[string]interface{}{"mode": mode}) + out := map[string]interface{}{"mode": mode} + if mode == skillinject.ModeDisabled { + out["removed"] = removed + } + outputOK(out) return } modeDesc := map[string]string{ diff --git a/go.mod b/go.mod index 09d02b11..3e3a1bc7 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/pilot-protocol/pilotprotocol -go 1.25.11 +go 1.25.12 require ( github.com/coder/websocket v1.8.15