@mycadet/assistant 0.12.9 → 0.12.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +0 -8
- package/eslint-rules/__tests__/cli-no-daemon-internals.test.ts +1 -1
- package/openapi.yaml +62 -910
- package/package.json +3 -7
- package/scripts/build-test-fixtures.ts +1 -1
- package/scripts/generate-openapi.ts +22 -6
- package/scripts/postinstall.ts +4 -10
- package/scripts/run-tests.ts +0 -21
- package/src/__tests__/adaptive-thinking-repair.test.ts +19 -57
- package/src/__tests__/agent-loop.test.ts +0 -76
- package/src/__tests__/anthropic-provider.test.ts +6 -6
- package/src/__tests__/assert-not-live-db.ts +1 -1
- package/src/__tests__/assistant-id-boundary-guard.test.ts +3 -3
- package/src/__tests__/attachments-store-heic-normalize.test.ts +1 -1
- package/src/__tests__/attachments-store.test.ts +1 -1
- package/src/__tests__/audit-log-rotation.test.ts +1 -1
- package/src/__tests__/btw-routes.test.ts +1 -1
- package/src/__tests__/byok-default-profile-ensure.test.ts +21 -27
- package/src/__tests__/call-conversation-messages.test.ts +0 -5
- package/src/__tests__/call-recovery.test.ts +2 -7
- package/src/__tests__/call-routes-http.test.ts +0 -12
- package/src/__tests__/call-store.test.ts +2 -7
- package/src/__tests__/catalog-files.test.ts +7 -5
- package/src/__tests__/checker.test.ts +1 -1
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +4 -4
- package/src/__tests__/cloud-client-verify-credential.test.ts +0 -4
- package/src/__tests__/cloud-credential-catalog-cli.test.ts +14 -14
- package/src/__tests__/cloud-profile-guard.test.ts +48 -401
- package/src/__tests__/compaction-events.test.ts +2 -2
- package/src/__tests__/config-loader-backfill.test.ts +74 -163
- package/src/__tests__/config-loader-cloud-defaults.test.ts +11 -11
- package/src/__tests__/config-loader-corrupt.test.ts +10 -10
- package/src/__tests__/config-loader-quarantine-notice.test.ts +7 -7
- package/src/__tests__/config-loader-validation-reset-notice.test.ts +11 -11
- package/src/__tests__/config-schema.test.ts +26 -27
- package/src/__tests__/config-set-route.test.ts +2 -6
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +28 -300
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +39 -246
- package/src/__tests__/conversation-agent-loop.test.ts +2 -58
- package/src/__tests__/conversation-clear-safety.test.ts +0 -30
- package/src/__tests__/conversation-inference-profile-route.test.ts +1 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +1 -1
- package/src/__tests__/conversation-skill-tools.test.ts +4 -444
- package/src/__tests__/conversation-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-store.test.ts +1 -84
- package/src/__tests__/conversation-surface-state.test.ts +0 -16
- package/src/__tests__/credential-health-service.test.ts +14 -1
- package/src/__tests__/credential-record-retire.test.ts +1 -1
- package/src/__tests__/credential-record-write-through.test.ts +1 -1
- package/src/__tests__/credential-security-invariants.test.ts +0 -145
- package/src/__tests__/custom-profile-ensure.test.ts +1 -1
- package/src/__tests__/default-profile-catalog-fallback.test.ts +5 -15
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -2
- package/src/__tests__/dictation-profile-store.test.ts +6 -6
- package/src/__tests__/disk-usage.test.ts +28 -164
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +1 -1
- package/src/__tests__/embedding-cloud-proxy-selection.test.ts +1 -1
- package/src/__tests__/emit-event-signal.test.ts +1 -1
- package/src/__tests__/gateway-client-cloud-outbound.test.ts +3 -3
- package/src/__tests__/gateway-only-enforcement.test.ts +1 -1
- package/src/__tests__/guardian-dispatch.test.ts +0 -13
- package/src/__tests__/helpers/seed-app.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -1
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/identity-routes.test.ts +1 -1
- package/src/__tests__/inference-profile-session-handler.test.ts +1 -1
- package/src/__tests__/inference-profile-session-ipc.test.ts +1 -1
- package/src/__tests__/injector-chain.test.ts +1 -1
- package/src/__tests__/injector-config-quarantine-notice.test.ts +1 -1
- package/src/__tests__/injector-config-validation-reset-notice.test.ts +1 -1
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +1 -1
- package/src/__tests__/{platform.test.ts → layout.test.ts} +1 -1
- package/src/__tests__/list-all-apps.test.ts +1 -1
- package/src/__tests__/live-workspace-guard.test.ts +2 -2
- package/src/__tests__/llm-resolver-override-or-default.test.ts +5 -6
- package/src/__tests__/llm-resolver.test.ts +7 -7
- package/src/__tests__/llm-schema-fallback-profile.test.ts +9 -10
- package/src/__tests__/llm-usage-store.test.ts +0 -495
- package/src/__tests__/log-export-routes.test.ts +1 -1
- package/src/__tests__/mcp-config-secret-boundary.test.ts +12 -53
- package/src/__tests__/mcp-list-plugin-servers.test.ts +1 -1
- package/src/__tests__/memory-config-file-parity.test.ts +1 -1
- package/src/__tests__/memory-v2-static-injector.test.ts +1 -1
- package/src/__tests__/messages-read-boundary-guard.test.ts +0 -2
- package/src/__tests__/notification-guardian-path.test.ts +0 -7
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +8 -8
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/persona-resolver.test.ts +1 -1
- package/src/__tests__/plugin-app-serve-routes.test.ts +1 -1
- package/src/__tests__/plugin-effective-enabled-set.test.ts +1 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +4 -6
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -3
- package/src/__tests__/provider-cloud-proxy-integration.test.ts +1 -5
- package/src/__tests__/provider-connections-backfill.test.ts +10 -10
- package/src/__tests__/prune-old-conversations-job.test.ts +6 -31
- package/src/__tests__/resolve-app.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +1 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +0 -53
- package/src/__tests__/schedule-routes.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +1 -1
- package/src/__tests__/secret-routes-cloud-proxy.test.ts +1 -1
- package/src/__tests__/skill-load-plugin-scope.test.ts +1 -1
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -1
- package/src/__tests__/tool-audit.test.ts +5 -235
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +54 -114
- package/src/__tests__/tools-get-route.test.ts +1 -1
- package/src/__tests__/turn-outcome-read.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/usage-attribution.test.ts +0 -31
- package/src/__tests__/verification-control-plane-policy.test.ts +3 -10
- package/src/__tests__/visible-app-context.test.ts +1 -1
- package/src/__tests__/voice-ingress-preflight.test.ts +1 -1
- package/src/__tests__/workspace-git-service.test.ts +1 -1
- package/src/__tests__/workspace-greetings.test.ts +1 -1
- package/src/acp/__tests__/helpers/acp-config-stub.ts +1 -1
- package/src/activation/progress-store.ts +1 -1
- package/src/activation/turn-hooks.ts +1 -1
- package/src/agent/loop.ts +0 -51
- package/src/apps/app-store.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +2 -172
- package/src/avatar/accent-backfill.ts +1 -1
- package/src/avatar/avatar-manifest.ts +1 -1
- package/src/avatar/avatar-store.ts +10 -69
- package/src/avatar/ensure-raster.ts +1 -1
- package/src/avatar/identity-avatar.ts +1 -1
- package/src/avatar/sync-avatar.ts +1 -1
- package/src/avatar/traits-png-sync.ts +1 -1
- package/src/bundler/compiler-tools.ts +1 -1
- package/src/bundler/package-resolver.ts +1 -1
- package/src/calls/__tests__/call-controller.test.ts +25 -27
- package/src/calls/active-call-lease.ts +1 -1
- package/src/calls/call-controller.ts +3 -3
- package/src/calls/call-domain.ts +4 -8
- package/src/calls/call-store.ts +2 -76
- package/src/calls/resolve-call-tts-provider.ts +3 -3
- package/src/calls/twilio-config.ts +3 -3
- package/src/calls/twilio-rest.ts +2 -2
- package/src/calls/voice-ingress-preflight.ts +2 -2
- package/src/cli/bundled-modules.ts +0 -2
- package/src/cli/commands/__tests__/apps.test.ts +1 -1
- package/src/cli/commands/__tests__/plugins.test.ts +9 -66
- package/src/cli/commands/__tests__/ps.test.ts +67 -0
- package/src/cli/commands/auth.help.ts +2 -2
- package/src/cli/commands/avatar.ts +1 -1
- package/src/cli/commands/changelog.ts +1 -1
- package/src/cli/commands/cloud/__tests__/subscription.test.ts +2 -2
- package/src/cli/commands/cloud/index.help.ts +1 -1
- package/src/cli/commands/cloud/index.ts +2 -2
- package/src/cli/commands/db/refresh.ts +1 -1
- package/src/cli/commands/db/repair-step-conversation-backfill.ts +1 -1
- package/src/cli/commands/db/repair.ts +1 -1
- package/src/cli/commands/db/status.ts +1 -1
- package/src/cli/commands/inference-profiles.ts +1 -12
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +1 -116
- package/src/cli/commands/memory/index.help.ts +1 -43
- package/src/cli/commands/memory/memory-v3.ts +0 -64
- package/src/cli/commands/plugins.ts +54 -205
- package/src/cli/commands/ps.ts +5 -2
- package/src/cli/commands/status.ts +1 -1
- package/src/cli/index.help.ts +0 -2
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +36 -66
- package/src/cli/lib/__tests__/install-from-cloud.test.ts +1 -1
- package/src/cli/lib/__tests__/install-from-github.test.ts +41 -117
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +2 -185
- package/src/cli/lib/__tests__/plugin-catalog-resolve.test.ts +36 -66
- package/src/cli/lib/__tests__/plugin-catalog-visibility.test.ts +4 -6
- package/src/cli/lib/__tests__/plugin-details.test.ts +113 -96
- package/src/cli/lib/__tests__/search-plugins.test.ts +9 -16
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +1 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +38 -15
- package/src/cli/lib/diff-plugin.ts +1 -1
- package/src/cli/lib/inspect-plugin.ts +13 -97
- package/src/cli/lib/install-from-cloud.ts +1 -1
- package/src/cli/lib/install-from-github.ts +75 -154
- package/src/cli/lib/install-plugin-dependencies.ts +1 -1
- package/src/cli/lib/list-installed-plugins.ts +1 -1
- package/src/cli/lib/open-browser.ts +1 -1
- package/src/cli/lib/plugin-catalog-cache.ts +8 -51
- package/src/cli/lib/plugin-catalog-resolve.ts +3 -22
- package/src/cli/lib/plugin-details.ts +5 -27
- package/src/cli/lib/plugin-marketplace.ts +18 -266
- package/src/cli/lib/search-plugins.ts +23 -43
- package/src/cli/lib/toggle-plugin.ts +1 -1
- package/src/cli/lib/uninstall-plugin.ts +1 -1
- package/src/cli/lib/upgrade-plugin.ts +3 -49
- package/src/cli/program.ts +1 -3
- package/src/cli.ts +1 -1
- package/src/cloud/client.ts +0 -6
- package/src/cloud/consent-cache.test.ts +22 -201
- package/src/cloud/consent-cache.ts +16 -111
- package/src/cloud/sync-identity.ts +1 -1
- package/src/config/__tests__/chat-settings.test.ts +1 -1
- package/src/config/__tests__/cloud-identity.test.ts +15 -2
- package/src/config/__tests__/default-plugin-skill-discovery.test.ts +1 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +12 -51
- package/src/config/__tests__/deployment-context-defaults.test.ts +13 -13
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +4 -4
- package/src/config/__tests__/loader-last-known-good.test.ts +9 -9
- package/src/config/__tests__/loader-sparse-array-cleanup.test.ts +7 -7
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +1 -1
- package/src/config/__tests__/profile-materialization.test.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +1 -106
- package/src/config/acp-agent-write.ts +1 -1
- package/src/config/bundled-skills/settings/tools/open-system-settings.test.ts +1 -1
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/cloud-identity.ts +12 -5
- package/src/config/composer-preferences.ts +1 -1
- package/src/config/default-profile-catalog.ts +8 -75
- package/src/config/default-profile-names.ts +2 -25
- package/src/config/env-registry.ts +1 -14
- package/src/config/env.ts +8 -6
- package/src/config/llm-resolver.ts +1 -2
- package/src/config/loader.ts +13 -17
- package/src/config/profile-materialization.ts +1 -1
- package/src/config/schema.ts +1 -7
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/llm.ts +22 -27
- package/src/config/seed-inference-profiles.ts +16 -17
- package/src/config/skills.ts +2 -2
- package/src/config/sync-gated-profiles.ts +6 -24
- package/src/credential-health/credential-health-service.ts +10 -3
- package/src/daemon/approval-generators.ts +3 -3
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +0 -18
- package/src/daemon/conversation-agent-loop.ts +6 -97
- package/src/daemon/conversation-process.ts +1 -1
- package/src/daemon/conversation-skill-tools.ts +4 -72
- package/src/daemon/conversation-store.ts +1 -1
- package/src/daemon/conversation-surface-state.ts +0 -33
- package/src/daemon/conversation-surfaces.ts +1 -86
- package/src/daemon/conversation-tool-setup.test.ts +1 -1
- package/src/daemon/conversation-tool-setup.ts +0 -23
- package/src/daemon/conversation.ts +3 -5
- package/src/daemon/daemon-control.ts +3 -3
- package/src/daemon/dictation-profile-store.ts +3 -3
- package/src/daemon/event-loop-watchdog.test.ts +2 -198
- package/src/daemon/event-loop-watchdog.ts +0 -185
- package/src/daemon/external-plugins-bootstrap.ts +1 -1
- package/src/daemon/first-greeting.ts +1 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/host-app-control-proxy.ts +1 -1
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/identity-helpers.ts +1 -1
- package/src/daemon/lifecycle.ts +11 -15
- package/src/daemon/main.ts +1 -1
- package/src/daemon/now-scratchpad.ts +1 -1
- package/src/daemon/process-message.ts +4 -4
- package/src/daemon/profiler-run-store.ts +2 -2
- package/src/daemon/providers-setup.ts +2 -2
- package/src/daemon/shutdown-handlers.ts +0 -10
- package/src/daemon/sqlite-corruption-watchdog.test.ts +2 -142
- package/src/daemon/sqlite-corruption-watchdog.ts +17 -81
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/desktop/desktop-session-manager.ts +1 -1
- package/src/desktop/desktop-wallpaper-renderer.ts +1 -1
- package/src/heartbeat/heartbeat-service.ts +1 -1
- package/src/home/feed-writer.ts +1 -1
- package/src/home/home-greeting-cache.ts +1 -1
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/hooks/__tests__/hook-live-reload.test.ts +1 -1
- package/src/hooks/hook-loader.ts +2 -2
- package/src/inbound/__tests__/oauth-callback-url.test.ts +2 -2
- package/src/inbound/oauth-callback-url.ts +3 -3
- package/src/ipc/__tests__/attachment-ipc.test.ts +1 -1
- package/src/ipc/socket-path.ts +1 -1
- package/src/live-voice/__tests__/live-voice-archive.test.ts +1 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +1 -1
- package/src/live-voice/live-voice-session.ts +3 -66
- package/src/mcp/__tests__/effective-config.test.ts +1 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +2 -2
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +2 -3
- package/src/mcp/__tests__/reload-signal-emission.test.ts +1 -1
- package/src/mcp/__tests__/reload-signal.test.ts +1 -1
- package/src/mcp/__tests__/workspace-mcp-config.test.ts +19 -21
- package/src/mcp/reload-signal.ts +1 -1
- package/src/mcp/workspace-mcp-config.ts +1 -23
- package/src/messaging/draft-store.ts +1 -1
- package/src/monitoring/__tests__/active-conversations.test.ts +1 -1
- package/src/monitoring/__tests__/page-cache.test.ts +1 -1
- package/src/monitoring/__tests__/stall-capture.test.ts +1 -1
- package/src/monitoring/__tests__/workspace-git-heartbeat.test.ts +1 -1
- package/src/monitoring/active-conversations.ts +1 -1
- package/src/monitoring/control.ts +1 -1
- package/src/monitoring/daemon-boot-time.ts +1 -1
- package/src/monitoring/daemon-heartbeat.ts +1 -1
- package/src/monitoring/page-cache.ts +1 -1
- package/src/monitoring/plugin-auto-update.ts +1 -7
- package/src/monitoring/plugin-source-watch.ts +1 -1
- package/src/monitoring/recovery/db.ts +1 -1
- package/src/monitoring/recovery/inflight-content.ts +1 -1
- package/src/monitoring/resource-sampler.ts +3 -14
- package/src/monitoring/stall-capture.ts +1 -1
- package/src/monitoring/worker.ts +9 -76
- package/src/monitoring/workspace-git-heartbeat.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +4 -4
- package/src/permissions/checker.ts +1 -1
- package/src/permissions/workspace-policy.ts +1 -1
- package/src/persistence/__tests__/main-db-readonly-probe.ts +27 -52
- package/src/persistence/__tests__/message-content-file.test.ts +1 -1
- package/src/persistence/__tests__/slow-sync-log.test.ts +0 -2
- package/src/persistence/attachments-store.ts +1 -1
- package/src/persistence/conversation-crud.ts +1 -38
- package/src/persistence/conversation-directories.ts +1 -1
- package/src/persistence/conversation-types.ts +0 -8
- package/src/persistence/db-async-query.ts +1 -1
- package/src/persistence/db-connection.ts +11 -1
- package/src/persistence/db-init.ts +1 -1
- package/src/persistence/db-maintenance.ts +1 -1
- package/src/persistence/embeddings/__tests__/embedding-local-lifecycle.test.ts +1 -1
- package/src/persistence/embeddings/embedding-local.ts +4 -4
- package/src/persistence/embeddings/embedding-runtime-manager.ts +1 -1
- package/src/persistence/embeddings/qdrant-client.ts +1 -1
- package/src/persistence/embeddings/qdrant-manager.ts +1 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-backfill.test.ts +1 -1
- package/src/persistence/job-handlers/__tests__/message-lexical-enqueue.test.ts +1 -1
- package/src/persistence/job-handlers/cleanup.ts +8 -17
- package/src/persistence/llm-usage-store.ts +2 -229
- package/src/persistence/message-content-file.ts +1 -1
- package/src/persistence/migrations/209-strip-thinking-from-consolidated.ts +1 -1
- package/src/persistence/migrations/222-strip-placeholder-sentinels-from-messages.ts +1 -1
- package/src/persistence/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +1 -1
- package/src/persistence/migrations/306-rewrite-frontier-profile-pins.test.ts +1 -1
- package/src/persistence/migrations/329-move-onboarding-events-to-telemetry-db.test.ts +13 -0
- package/src/persistence/migrations/330-move-auth-fallback-events-to-telemetry-db.test.ts +13 -0
- package/src/persistence/migrations/331-move-lifecycle-events-to-telemetry-db.test.ts +13 -0
- package/src/persistence/migrations/332-move-skill-loaded-events-to-telemetry-db.test.ts +13 -0
- package/src/persistence/migrations/333-create-telemetry-events-table.test.ts +7 -4
- package/src/persistence/migrations/334-backfill-telemetry-events-outbox.test.ts +13 -0
- package/src/persistence/migrations/387-drop-telemetry-tables.ts +51 -0
- package/src/persistence/migrations/__tests__/327-create-flush-checkpoints.test.ts +9 -1
- package/src/persistence/migrations/__tests__/387-drop-telemetry-tables.test.ts +79 -0
- package/src/persistence/migrations/helpers/relocation.ts +1 -1
- package/src/persistence/migrations/run-migrations.ts +1 -1
- package/src/persistence/migrations/validate-migration-state.ts +1 -1
- package/src/persistence/raw-query.ts +1 -29
- package/src/persistence/slow-sync-log.ts +0 -23
- package/src/persistence/steps.ts +2 -0
- package/src/{telemetry → persistence}/tool-audit.ts +6 -121
- package/src/plugin-api/index.ts +1 -1
- package/src/plugin-api/plugin-name-env.ts +1 -1
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +1 -1
- package/src/plugins/collect-source-versions.ts +1 -1
- package/src/plugins/defaults/channel/package.json +1 -1
- package/src/plugins/defaults/cloud-hosted/package.json +1 -1
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/documents/package.json +1 -1
- package/src/plugins/defaults/empty-response/package.json +1 -1
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-fallback/package.json +1 -1
- package/src/plugins/defaults/image-recovery/package.json +1 -1
- package/src/plugins/defaults/max-tokens-continue/package.json +1 -1
- package/src/plugins/defaults/memory/__tests__/db-async-query.test.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/db-maintenance.test.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +0 -82
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-skill-card.test.ts +0 -41
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -2
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +3 -52
- package/src/plugins/defaults/memory/memory-retrospective-skill-card.ts +0 -18
- package/src/plugins/defaults/memory/package.json +1 -1
- package/src/plugins/defaults/memory/paths.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-worker-routes.test.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +5 -5
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +0 -207
- package/src/plugins/defaults/memory/src/memory-worker-routes.ts +1 -1
- package/src/plugins/defaults/memory/startup.ts +1 -22
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +2 -253
- package/src/plugins/defaults/memory/v3/orchestrate.ts +5 -129
- package/src/plugins/defaults/memory/worker-control.ts +1 -1
- package/src/plugins/defaults/memory/worker.ts +2 -2
- package/src/plugins/defaults/session/package.json +1 -1
- package/src/plugins/defaults/surface-completion-nudge/package.json +1 -1
- package/src/plugins/defaults/task-progress/package.json +1 -1
- package/src/plugins/defaults/task-progress-nudge/package.json +1 -1
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +1 -1
- package/src/plugins/defaults/turn-context/package.json +1 -1
- package/src/plugins/defaults/workspace/injectors.ts +1 -1
- package/src/plugins/defaults/workspace/package.json +1 -1
- package/src/plugins/disabled-state.ts +1 -1
- package/src/plugins/ensure-plugin-api-shim.ts +1 -1
- package/src/plugins/ensure-shared-dep-links.ts +1 -1
- package/src/plugins/installed-plugin-dirs.ts +1 -1
- package/src/plugins/mcp-servers.ts +7 -9
- package/src/plugins/mtime-cache.ts +2 -2
- package/src/plugins/source-versions.ts +1 -1
- package/src/prompts/bootstrap-cleanup.ts +1 -1
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/sections.ts +2 -2
- package/src/prompts/system-prompt.ts +10 -42
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +0 -37
- package/src/providers/__tests__/cadet-connection-routing.test.ts +2 -2
- package/src/providers/cadet/client.ts +1 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -1
- package/src/providers/inference/adapter-factory.ts +1 -1
- package/src/providers/inference/backfill.ts +3 -10
- package/src/providers/inference/profile-probe.ts +1 -1
- package/src/providers/retry.ts +0 -2
- package/src/providers/types.ts +0 -7
- package/src/providers/usage-tracking.ts +1 -1
- package/src/routes/__tests__/route-host.test.ts +1 -1
- package/src/routes/control.ts +1 -1
- package/src/routes/route-host-client.ts +1 -1
- package/src/routes/worker.ts +2 -2
- package/src/runtime/agent-wake.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +1 -1
- package/src/runtime/auth/token-service.ts +1 -1
- package/src/runtime/background-job-runner.ts +1 -1
- package/src/runtime/middleware/twilio-validation.ts +2 -2
- package/src/runtime/routes/__tests__/app-pin-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/apps-refresh-route.test.ts +1 -1
- package/src/runtime/routes/__tests__/avatar-state-routes.test.ts +3 -33
- package/src/runtime/routes/__tests__/cloud-credits-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/cloud-invoice-routes.test.ts +4 -3
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +59 -287
- package/src/runtime/routes/__tests__/default-plugin-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/deprecated-route-context.test.ts +15 -62
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +105 -12
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +47 -211
- package/src/runtime/routes/__tests__/route-host-worker-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +1 -1
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-routes-cli.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-routes-notices.test.ts +1 -1
- package/src/runtime/routes/__tests__/user-routes-request.test.ts +1 -1
- package/src/runtime/routes/app-management-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +1 -1
- package/src/runtime/routes/audit-routes.ts +1 -1
- package/src/runtime/routes/avatar-routes.ts +3 -10
- package/src/runtime/routes/cloud-routes.ts +3 -3
- package/src/runtime/routes/content-source-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +60 -621
- package/src/runtime/routes/conversation-routes.ts +1 -38
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/deprecated-route-context.ts +5 -61
- package/src/runtime/routes/heartbeat-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +1 -1
- package/src/runtime/routes/index.ts +0 -6
- package/src/runtime/routes/inference-profile-session-handler.ts +3 -3
- package/src/runtime/routes/inference-profiles-routes.ts +48 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +3 -3
- package/src/runtime/routes/log-export/__tests__/installed-inventory.test.ts +1 -1
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +1 -1
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +1 -1
- package/src/runtime/routes/log-export/workspace-allowlist.ts +1 -1
- package/src/runtime/routes/log-export-routes.ts +17 -15
- package/src/runtime/routes/migration-rollback-routes.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +2 -3
- package/src/runtime/routes/monitoring-routes.ts +2 -2
- package/src/runtime/routes/oauth-providers.ts +5 -5
- package/src/runtime/routes/plugins-routes.ts +38 -163
- package/src/runtime/routes/profiler-routes.ts +1 -1
- package/src/runtime/routes/route-host-worker-routes.ts +1 -1
- package/src/runtime/routes/sanity-routes.ts +1 -1
- package/src/runtime/routes/schedule-routes.ts +1 -1
- package/src/runtime/routes/schedule-worker-routes.ts +1 -1
- package/src/runtime/routes/settings-routes.ts +1 -1
- package/src/runtime/routes/sounds-config-routes.ts +1 -1
- package/src/runtime/routes/surface-conversation-resolver.ts +1 -7
- package/src/runtime/routes/user-route-dispatcher.ts +1 -3
- package/src/runtime/routes/user-route-import.ts +1 -1
- package/src/runtime/routes/user-route-resolution.ts +1 -1
- package/src/runtime/routes/user-routes-cli.ts +1 -1
- package/src/runtime/routes/workspace-commit-routes.ts +1 -1
- package/src/runtime/routes/workspace-greetings.ts +1 -1
- package/src/runtime/routes/workspace-routes.ts +1 -1
- package/src/runtime/routes/workspace-utils.ts +1 -1
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +1 -1
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1 -1
- package/src/schedule/plugin-schedule-availability.ts +1 -1
- package/src/schedule/plugin-schedule-declarations.ts +1 -1
- package/src/schedule/run-script.ts +1 -1
- package/src/schedule/scheduler.ts +1 -11
- package/src/schedule/worker-control.ts +1 -1
- package/src/schedule/worker.ts +3 -3
- package/src/security/oauth2.ts +2 -2
- package/src/security/secret-allowlist.ts +1 -1
- package/src/signals/cancel.ts +1 -1
- package/src/signals/conversation-undo.ts +1 -1
- package/src/signals/emit-event.ts +1 -1
- package/src/signals/event-stream.ts +1 -1
- package/src/signals/user-message.ts +1 -1
- package/src/skills/catalog-install.ts +2 -1
- package/src/skills/clawhub.ts +1 -1
- package/src/skills/cloud-store.ts +1 -1
- package/src/skills/path-classifier.ts +1 -1
- package/src/skills/skill-history.ts +1 -1
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/skills/uninstall.ts +1 -1
- package/src/skills/workspace-skill-md-signature.ts +1 -1
- package/src/subagent/manager.ts +1 -1
- package/src/theme/workspace-theme.ts +1 -1
- package/src/tools/browser/auth-cache.ts +1 -1
- package/src/tools/browser/browser-manager.ts +1 -1
- package/src/tools/browser/runtime-check.ts +1 -1
- package/src/tools/executor.ts +4 -4
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/permission-checker.ts +2 -51
- package/src/tools/run-standalone.ts +1 -1
- package/src/tools/skills/format-input-schema.ts +1 -1
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/skills/scaffold-cloud.ts +0 -32
- package/src/tools/terminal/safe-env.ts +1 -2
- package/src/tools/terminal/sanitized-bash.ts +1 -1
- package/src/tools/terminal/shell.ts +1 -1
- package/src/tools/tool-approval-handler.ts +4 -1
- package/src/tools/workflows/manage-workflows.ts +2 -2
- package/src/tools/workflows/run-workflow.test.ts +1 -1
- package/src/tools/workspace-tools/loader.ts +1 -1
- package/src/tts/synthesis-stream.ts +2 -2
- package/src/usage/attribution.ts +1 -11
- package/src/util/browser.ts +1 -1
- package/src/util/bun-runtime.ts +2 -1
- package/src/util/device-id.ts +1 -1
- package/src/util/disk-usage.ts +4 -139
- package/src/util/layout.ts +3 -3
- package/src/util/logger.ts +1 -1
- package/src/util/logs-db-path.ts +1 -1
- package/src/util/memory-db-path.ts +1 -1
- package/src/util/{platform.ts → os.ts} +5 -30
- package/src/util/process-tree.ts +19 -0
- package/src/util/sqlite3-runtime.ts +1 -1
- package/src/util/telemetry-db-path.ts +1 -1
- package/src/watcher/__tests__/engine.test.ts +0 -23
- package/src/watcher/engine.ts +0 -7
- package/src/windows-compiled-cli.ts +0 -4
- package/src/windows-compiled-worker-entry.ts +0 -6
- package/src/workflows/leaf-runner.test.ts +1 -1
- package/src/workflows/leaf-runner.ts +1 -1
- package/src/workflows/library.ts +1 -1
- package/src/workspace/adaptive-thinking-repair.ts +5 -24
- package/src/workspace/byok-default-profile-ensure.ts +24 -26
- package/src/workspace/custom-profile-ensure.ts +2 -2
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/hatched-date.ts +1 -1
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +2 -2
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +1 -1
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +1 -1
- package/src/workspace/migrations/077-seed-memory-router-callsite.ts +1 -1
- package/src/workspace/migrations/registry.ts +0 -4
- package/src/workspace/turn-commit.ts +1 -1
- package/docs/activation-funnel-telemetry.md +0 -324
- package/scripts/bundled-plugin-packages.ts +0 -154
- package/scripts/generate-bundled-plugin-packages.ts +0 -29
- package/src/__tests__/activation-early-marking.test.ts +0 -114
- package/src/__tests__/auth-fallback-events-store.test.ts +0 -99
- package/src/__tests__/config-get-vision-flag.test.ts +0 -139
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +0 -400
- package/src/__tests__/internal-telemetry-routes.test.ts +0 -213
- package/src/__tests__/telemetry-routes.test.ts +0 -182
- package/src/__tests__/turn-events-store.test.ts +0 -376
- package/src/__tests__/ui-channel-variants.test.ts +0 -53
- package/src/avatar/__tests__/avatar-changed-telemetry.test.ts +0 -217
- package/src/avatar/avatar-changed-telemetry.ts +0 -128
- package/src/calls/__tests__/call-funnel-telemetry.test.ts +0 -204
- package/src/cli/commands/telemetry.help.ts +0 -27
- package/src/cli/commands/telemetry.ts +0 -83
- package/src/cli/lib/__tests__/local-plugin-upgrade.test.ts +0 -251
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +0 -113
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +0 -384
- package/src/cli/lib/__tests__/plugin-pin-history.test.ts +0 -164
- package/src/cli/lib/__tests__/plugins-install-offline.test.ts +0 -228
- package/src/cli/lib/bundled-marketplace.json +0 -1575
- package/src/cli/lib/bundled-plugin-packages.json +0 -827
- package/src/cli/lib/bundled-plugin-packages.ts +0 -87
- package/src/cli/lib/plugin-catalog-local.ts +0 -94
- package/src/cli/lib/plugin-pin-history.ts +0 -276
- package/src/cloud/feature-gate.ts +0 -8
- package/src/config/__tests__/cloud-rehydration.test.ts +0 -180
- package/src/config/cloud-rehydration.ts +0 -35
- package/src/daemon/__tests__/auto-profile-router.test.ts +0 -380
- package/src/daemon/auto-profile-router.ts +0 -428
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +0 -391
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +0 -113
- package/src/monitoring/__tests__/oom-kill-reporter.test.ts +0 -374
- package/src/monitoring/db-integrity-check.ts +0 -163
- package/src/monitoring/db-integrity-sample.ts +0 -176
- package/src/monitoring/oom-kill-reporter.ts +0 -469
- package/src/onboarding/__tests__/onboarding-events-store.test.ts +0 -158
- package/src/onboarding/onboarding-events-store.ts +0 -296
- package/src/persistence/__tests__/raw-query-telemetry.test.ts +0 -76
- package/src/persistence/lifecycle-events-store.test.ts +0 -134
- package/src/persistence/lifecycle-events-store.ts +0 -110
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +0 -76
- package/src/plugins/defaults/memory/activation-session-store.ts +0 -50
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +0 -281
- package/src/runtime/routes/auto-profile-routes.ts +0 -83
- package/src/runtime/routes/internal-telemetry-routes.ts +0 -142
- package/src/runtime/routes/telemetry-routes.ts +0 -274
- package/src/security/auth-fallback-events-store.ts +0 -57
- package/src/telemetry/__tests__/activation-funnel.test.ts +0 -95
- package/src/telemetry/__tests__/config-setting-events-store.test.ts +0 -63
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +0 -143
- package/src/telemetry/__tests__/flush-checkpoints.test.ts +0 -56
- package/src/telemetry/__tests__/live-voice-funnel.test.ts +0 -174
- package/src/telemetry/__tests__/outbox-test-harness.ts +0 -85
- package/src/telemetry/__tests__/phone-call-funnel.test.ts +0 -72
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +0 -282
- package/src/telemetry/__tests__/watchdog-events-store.test.ts +0 -71
- package/src/telemetry/activation-funnel.ts +0 -172
- package/src/telemetry/config-setting-events-store.ts +0 -40
- package/src/telemetry/config-setting-snapshot.ts +0 -170
- package/src/telemetry/flush-checkpoints.ts +0 -70
- package/src/telemetry/live-voice-funnel.ts +0 -185
- package/src/telemetry/memory-corpus-size.test.ts +0 -120
- package/src/telemetry/memory-corpus-size.ts +0 -184
- package/src/telemetry/memory-tier-reporter.test.ts +0 -195
- package/src/telemetry/memory-tier-reporter.ts +0 -130
- package/src/telemetry/phone-call-funnel.ts +0 -144
- package/src/telemetry/skill-loaded-events-store.test.ts +0 -93
- package/src/telemetry/skill-loaded-events-store.ts +0 -39
- package/src/telemetry/telemetry-event-sources.test.ts +0 -223
- package/src/telemetry/telemetry-event-sources.ts +0 -597
- package/src/telemetry/telemetry-events-outbox.test.ts +0 -375
- package/src/telemetry/telemetry-events-outbox.ts +0 -237
- package/src/telemetry/telemetry-main-db.ts +0 -48
- package/src/telemetry/telemetry-wire-source.json +0 -4
- package/src/telemetry/telemetry-wire-validation.test.ts +0 -157
- package/src/telemetry/telemetry-wire-validation.ts +0 -162
- package/src/telemetry/telemetry-wire.generated.ts +0 -483
- package/src/telemetry/tool-executed-events-store.test.ts +0 -210
- package/src/telemetry/tool-executed-events-store.ts +0 -102
- package/src/telemetry/trace-collection-policy.test.ts +0 -28
- package/src/telemetry/trace-collection-policy.ts +0 -30
- package/src/telemetry/turn-events-store.ts +0 -216
- package/src/telemetry/turn-trace-store.test.ts +0 -884
- package/src/telemetry/turn-trace-store.ts +0 -399
- package/src/telemetry/types.test.ts +0 -38
- package/src/telemetry/types.ts +0 -780
- package/src/telemetry/usage-telemetry-reporter.test.ts +0 -3445
- package/src/telemetry/usage-telemetry-reporter.ts +0 -566
- package/src/telemetry/watchdog-direct-emit.test.ts +0 -112
- package/src/telemetry/watchdog-direct-emit.ts +0 -104
- package/src/telemetry/watchdog-events-store.ts +0 -31
- package/src/tools/ui-surface/channel-variants.ts +0 -52
- package/src/watcher/__tests__/telemetry.test.ts +0 -178
- package/src/watcher/telemetry.ts +0 -93
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +0 -165
- package/src/workspace/migrations/104-recheck-adaptive-thinking-model-implied-anthropic.ts +0 -151
- /package/src/{telemetry → persistence}/tool-usage-store.ts +0 -0
- /package/src/{telemetry → persistence}/turn-outcome.ts +0 -0
|
@@ -1,566 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Usage telemetry reporter.
|
|
3
|
-
*
|
|
4
|
-
* Periodically flushes unreported telemetry events (LLM usage, turns,
|
|
5
|
-
* lifecycle, ...) from the local SQLite databases and POSTs them to the
|
|
6
|
-
* platform telemetry endpoint. Each event type is a
|
|
7
|
-
* {@link TelemetryEventSource}; the reporter is a generic engine over the
|
|
8
|
-
* source list it is constructed with, acknowledging each source after a
|
|
9
|
-
* successful upload in one of two modes: watermark-mode sources advance a
|
|
10
|
-
* compound `(createdAt, id)` cursor in the telemetry DB's
|
|
11
|
-
* `flush_checkpoints` table, and ack-mode sources (those defining `ack`)
|
|
12
|
-
* delete their shipped rows instead.
|
|
13
|
-
*
|
|
14
|
-
* Flushing is split across two processes, each running its own reporter
|
|
15
|
-
* instance over a disjoint source partition: the daemon flushes turn events
|
|
16
|
-
* (their completeness barrier and consented traces read live in-memory
|
|
17
|
-
* conversation state), and the resource monitor process flushes everything
|
|
18
|
-
* else off the daemon's event loop — see `monitoring/worker.ts`. The
|
|
19
|
-
* per-source watermark cursors live in the telemetry DB, which both
|
|
20
|
-
* processes open read-write; each instance only touches its own sources'
|
|
21
|
-
* cursors, so the split shares no flush state.
|
|
22
|
-
*
|
|
23
|
-
* Authenticated-only: events are sent via the cloud proxy context
|
|
24
|
-
* (Api-Key header). When no cloud credentials are available, or when
|
|
25
|
-
* platform features are disabled (CADET_DISABLE_CLOUD in local mode), the
|
|
26
|
-
* flush is skipped and retried next cycle.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { CadetCloudClient } from "../cloud/client.js";
|
|
30
|
-
import { getRawShareAnalytics } from "../cloud/consent-cache.js";
|
|
31
|
-
import { areCloudFeaturesEnabled } from "../cloud/feature-gate.js";
|
|
32
|
-
import { getCloudOrganizationId, getCloudUserId } from "../config/env.js";
|
|
33
|
-
import { getDeviceId } from "../util/device-id.js";
|
|
34
|
-
import { getLogger } from "../util/logger.js";
|
|
35
|
-
import { APP_VERSION } from "../version.js";
|
|
36
|
-
import {
|
|
37
|
-
type FlushCheckpointStore,
|
|
38
|
-
telemetryDbFlushCheckpointStore,
|
|
39
|
-
} from "./flush-checkpoints.js";
|
|
40
|
-
import {
|
|
41
|
-
DAEMON_TELEMETRY_EVENT_SOURCES,
|
|
42
|
-
MONITOR_TELEMETRY_EVENT_SOURCES,
|
|
43
|
-
type TelemetryEventSource,
|
|
44
|
-
type TelemetryEventSourceBatch,
|
|
45
|
-
TOOL_EXECUTED_SOURCE_ID,
|
|
46
|
-
watermarkKeysForSource,
|
|
47
|
-
} from "./telemetry-event-sources.js";
|
|
48
|
-
import {
|
|
49
|
-
deleteTelemetryOutboxEventsBefore,
|
|
50
|
-
OUTBOX_MAX_ROW_AGE_MS,
|
|
51
|
-
} from "./telemetry-events-outbox.js";
|
|
52
|
-
import { useReadOnlyMainDbForTelemetry } from "./telemetry-main-db.js";
|
|
53
|
-
import { validateWireEvents } from "./telemetry-wire-validation.js";
|
|
54
|
-
|
|
55
|
-
const log = getLogger("usage-telemetry");
|
|
56
|
-
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
// Constants
|
|
59
|
-
// ---------------------------------------------------------------------------
|
|
60
|
-
|
|
61
|
-
// Written into the `*_id` watermark checkpoints by the opt-out flush branch.
|
|
62
|
-
// Sorts lexicographically above every real row ID (all event stores generate
|
|
63
|
-
// lowercase v4 UUIDs), so the compound cursor's same-millisecond arm
|
|
64
|
-
// (`createdAt == watermark AND id > afterId`) can never match an opt-out row.
|
|
65
|
-
const OPT_OUT_WATERMARK_ID_SENTINEL = "ffffffff-ffff-ffff-ffff-ffffffffffff";
|
|
66
|
-
const REPORT_INTERVAL_MS = 5 * 60 * 1000;
|
|
67
|
-
const INITIAL_FLUSH_DELAY_MS = 30_000; // Delay first flush to let CES handshake complete
|
|
68
|
-
const BATCH_SIZE = 500;
|
|
69
|
-
const MAX_CONSECUTIVE_BATCHES = 10;
|
|
70
|
-
const TELEMETRY_PATH = "/v1/telemetry/ingest/";
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Outcome of a {@link UsageTelemetryReporter.flush} call. `flushed: true` means
|
|
74
|
-
* at least one batch reached the cloud (2xx). `sent` is the count the daemon
|
|
75
|
-
* POSTed; `persisted` is the count the cloud confirmed written; `dropped` is
|
|
76
|
-
* `sent - persisted` — folding server-side drops (opt-out, unauthenticated) and
|
|
77
|
-
* events silently skipped at ingest validation (unknown/invalid type) into one
|
|
78
|
-
* "did not land" number. `flushed: false` carries a kebab-case skip reason.
|
|
79
|
-
*/
|
|
80
|
-
export type TelemetryFlushSummary =
|
|
81
|
-
| { flushed: true; sent: number; persisted: number; dropped: number }
|
|
82
|
-
| { flushed: false; reason: string };
|
|
83
|
-
|
|
84
|
-
interface FlushAccumulator {
|
|
85
|
-
sent: number;
|
|
86
|
-
persisted: number;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Read `persisted` from an ingest response body. Falls back to `fallback`
|
|
91
|
-
* (the POSTed count — assume all landed) on a malformed body or an older
|
|
92
|
-
* platform that omits the field. Never throws.
|
|
93
|
-
*/
|
|
94
|
-
function parsePersistedCount(body: string, fallback: number): number {
|
|
95
|
-
try {
|
|
96
|
-
const parsed: unknown = JSON.parse(body);
|
|
97
|
-
if (parsed && typeof parsed === "object") {
|
|
98
|
-
const p = (parsed as { persisted?: unknown }).persisted;
|
|
99
|
-
if (typeof p === "number" && Number.isFinite(p) && p >= 0) {
|
|
100
|
-
return p;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
} catch {
|
|
104
|
-
// Malformed/empty body — assume everything persisted.
|
|
105
|
-
}
|
|
106
|
-
return fallback;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ---------------------------------------------------------------------------
|
|
110
|
-
// Singleton access
|
|
111
|
-
// ---------------------------------------------------------------------------
|
|
112
|
-
|
|
113
|
-
let _instance: UsageTelemetryReporter | null = null;
|
|
114
|
-
|
|
115
|
-
export function getUsageTelemetryReporter(): UsageTelemetryReporter | null {
|
|
116
|
-
return _instance;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function startReporterWithSources(
|
|
120
|
-
sources: readonly TelemetryEventSource[],
|
|
121
|
-
label: string,
|
|
122
|
-
): void {
|
|
123
|
-
if (process.env.CADET_DEV === "1") {
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
if (_instance) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
_instance = new UsageTelemetryReporter(sources);
|
|
130
|
-
_instance.start();
|
|
131
|
-
log.info(`${label} telemetry reporter started`);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Construct and start the daemon's singleton telemetry reporter, flushing
|
|
136
|
-
* the daemon-owned sources (turn events). No-op in dev mode (CADET_DEV=1)
|
|
137
|
-
* and idempotent if already started.
|
|
138
|
-
*
|
|
139
|
-
* Started even when share_analytics consent is opted out: flush() re-checks
|
|
140
|
-
* consent each cycle and, when opted out, sends nothing but advances its
|
|
141
|
-
* sources' watermarks (including the final flush in stop()). Not gated on DB
|
|
142
|
-
* readiness: getDb() can still work when initializeDb() failed mid-migration,
|
|
143
|
-
* in which case message writes keep producing rows the opt-out branch must
|
|
144
|
-
* keep covered. The reporter is degraded-mode safe — flush() treats DB errors
|
|
145
|
-
* as non-fatal.
|
|
146
|
-
*
|
|
147
|
-
* Also initializes the tool_executed absent-watermark epoch even though that
|
|
148
|
-
* source is flushed by the monitor process: the "no flush has ever advanced
|
|
149
|
-
* it" guard needs the before-any-tool-runs ordering only daemon startup can
|
|
150
|
-
* provide (the monitor boots concurrently with the daemon serving turns).
|
|
151
|
-
*/
|
|
152
|
-
export function startUsageTelemetryReporter(): void {
|
|
153
|
-
if (process.env.CADET_DEV === "1") {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
initToolExecutedWatermarkIfAbsent(telemetryDbFlushCheckpointStore);
|
|
157
|
-
startReporterWithSources(DAEMON_TELEMETRY_EVENT_SOURCES, "Daemon");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Construct and start the resource monitor process's singleton telemetry
|
|
162
|
-
* reporter, flushing every non-turn source off the daemon's event loop.
|
|
163
|
-
* No-op in dev mode (CADET_DEV=1) and idempotent if already started.
|
|
164
|
-
*
|
|
165
|
-
* Switches this process's telemetry main-DB reads to the dedicated
|
|
166
|
-
* read-only connection first: the monitor observes the daemon's main DB
|
|
167
|
-
* (WAL permits cross-process readers) and must never write to it or create
|
|
168
|
-
* it. The caller (monitoring/worker.ts) is responsible for the consent
|
|
169
|
-
* refresh loop the share_analytics gate reads.
|
|
170
|
-
*/
|
|
171
|
-
export function startMonitorUsageTelemetryReporter(): void {
|
|
172
|
-
if (process.env.CADET_DEV === "1") {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
useReadOnlyMainDbForTelemetry();
|
|
176
|
-
startReporterWithSources(MONITOR_TELEMETRY_EVENT_SOURCES, "Monitor");
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Stop the singleton usage telemetry reporter (final flush + timer teardown)
|
|
181
|
-
* and clear it. No-op when the reporter was never started (e.g. dev mode).
|
|
182
|
-
*/
|
|
183
|
-
export async function stopUsageTelemetryReporter(): Promise<void> {
|
|
184
|
-
if (!_instance) {
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
187
|
-
try {
|
|
188
|
-
await _instance.stop();
|
|
189
|
-
} finally {
|
|
190
|
-
_instance = null;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Initialize the tool_executed flush watermark to "now" when absent.
|
|
196
|
-
*
|
|
197
|
-
* `tool_invocations` is an always-on audit table that predates the reporter
|
|
198
|
-
* shipping its rows: an absent watermark means no flush (opted in or out) has
|
|
199
|
-
* ever advanced it, so rows recorded before this build — including any
|
|
200
|
-
* opted-out period under older builds that gated the reporter on the
|
|
201
|
-
* usage-data opt-out — would otherwise ship retroactively. Runs at daemon
|
|
202
|
-
* startup, before any tool can run, so no legitimate row falls behind the
|
|
203
|
-
* epoch; the checkpoint persists immediately so a crash can't leave it absent
|
|
204
|
-
* and re-initialize later. An EXISTING watermark is never touched: opted-out
|
|
205
|
-
* sessions keep it advancing via the opt-out flush branch, and overwriting it
|
|
206
|
-
* would drop a legitimate unshipped backlog. The other sources need no init:
|
|
207
|
-
* their recording is either consent-gated at write time (opt-out rows never
|
|
208
|
-
* exist) or covered by the same watermark discipline from first boot.
|
|
209
|
-
*
|
|
210
|
-
* Best-effort: DB init failures are tolerated at daemon startup (degraded
|
|
211
|
-
* mode), so this never throws — matching flush(), which treats DB errors as
|
|
212
|
-
* non-fatal.
|
|
213
|
-
*/
|
|
214
|
-
export function initToolExecutedWatermarkIfAbsent(
|
|
215
|
-
checkpoints: FlushCheckpointStore,
|
|
216
|
-
): void {
|
|
217
|
-
try {
|
|
218
|
-
const key = watermarkKeysForSource(TOOL_EXECUTED_SOURCE_ID).at;
|
|
219
|
-
if (checkpoints.get(key) == null) {
|
|
220
|
-
checkpoints.set(key, String(Date.now()));
|
|
221
|
-
}
|
|
222
|
-
} catch (err) {
|
|
223
|
-
log.warn(
|
|
224
|
-
{ err },
|
|
225
|
-
"tool_executed watermark init failed — non-fatal; a later run with a working DB re-runs the absent-checkpoint init",
|
|
226
|
-
);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// ---------------------------------------------------------------------------
|
|
231
|
-
// Reporter
|
|
232
|
-
// ---------------------------------------------------------------------------
|
|
233
|
-
|
|
234
|
-
export class UsageTelemetryReporter {
|
|
235
|
-
private initialFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
236
|
-
private timer: ReturnType<typeof setInterval> | null = null;
|
|
237
|
-
private activeFlush: Promise<TelemetryFlushSummary> | null = null;
|
|
238
|
-
private readonly sources: readonly TelemetryEventSource[];
|
|
239
|
-
private readonly checkpoints: FlushCheckpointStore;
|
|
240
|
-
|
|
241
|
-
constructor(
|
|
242
|
-
sources: readonly TelemetryEventSource[],
|
|
243
|
-
checkpoints: FlushCheckpointStore = telemetryDbFlushCheckpointStore,
|
|
244
|
-
) {
|
|
245
|
-
this.sources = sources;
|
|
246
|
-
this.checkpoints = checkpoints;
|
|
247
|
-
// Backstop for the tool_executed absent-watermark epoch (see
|
|
248
|
-
// initToolExecutedWatermarkIfAbsent) — the authoritative init runs at
|
|
249
|
-
// daemon startup, which alone guarantees the before-any-tool-runs
|
|
250
|
-
// ordering; constructing an instance that flushes the source re-runs the
|
|
251
|
-
// guarded init in case that never happened (e.g. a standalone monitor
|
|
252
|
-
// started out of band).
|
|
253
|
-
if (this.sources.some((s) => s.id === TOOL_EXECUTED_SOURCE_ID)) {
|
|
254
|
-
initToolExecutedWatermarkIfAbsent(this.checkpoints);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
start(): void {
|
|
259
|
-
// Delay the first flush to allow the credential infrastructure (CES
|
|
260
|
-
// handshake) to complete. Without this delay, CadetCloudClient.create()
|
|
261
|
-
// returns null because the credential backend hasn't resolved yet, causing
|
|
262
|
-
// the initial flush to be skipped (we send authenticated-only); the
|
|
263
|
-
// delay lets credentials resolve so the first flush can actually ship.
|
|
264
|
-
this.initialFlushTimer = setTimeout(() => {
|
|
265
|
-
this.initialFlushTimer = null;
|
|
266
|
-
this.flush().catch((err) => {
|
|
267
|
-
log.warn({ err }, "Initial usage telemetry flush failed");
|
|
268
|
-
});
|
|
269
|
-
}, INITIAL_FLUSH_DELAY_MS);
|
|
270
|
-
this.timer = setInterval(() => {
|
|
271
|
-
this.flush().catch((err) => {
|
|
272
|
-
log.warn({ err }, "Scheduled usage telemetry flush failed");
|
|
273
|
-
});
|
|
274
|
-
}, REPORT_INTERVAL_MS);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
async stop(): Promise<void> {
|
|
278
|
-
if (this.initialFlushTimer) {
|
|
279
|
-
clearTimeout(this.initialFlushTimer);
|
|
280
|
-
this.initialFlushTimer = null;
|
|
281
|
-
}
|
|
282
|
-
if (this.timer) {
|
|
283
|
-
clearInterval(this.timer);
|
|
284
|
-
this.timer = null;
|
|
285
|
-
}
|
|
286
|
-
if (this.activeFlush) {
|
|
287
|
-
await this.activeFlush;
|
|
288
|
-
}
|
|
289
|
-
await this.flush();
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
async flush(): Promise<TelemetryFlushSummary> {
|
|
293
|
-
if (this.activeFlush) {
|
|
294
|
-
return this.activeFlush; // overlap guard — share the in-flight result
|
|
295
|
-
}
|
|
296
|
-
this.activeFlush = this._doFlush();
|
|
297
|
-
try {
|
|
298
|
-
return await this.activeFlush;
|
|
299
|
-
} finally {
|
|
300
|
-
this.activeFlush = null;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
private async _doFlush(
|
|
305
|
-
batchCount = 0,
|
|
306
|
-
acc: FlushAccumulator = { sent: 0, persisted: 0 },
|
|
307
|
-
): Promise<TelemetryFlushSummary> {
|
|
308
|
-
// Once any batch has shipped (acc.sent > 0), a later skip/defer/error is
|
|
309
|
-
// just the natural end of the flush — report what landed, not a failure.
|
|
310
|
-
const flushed = (): TelemetryFlushSummary => ({
|
|
311
|
-
flushed: true,
|
|
312
|
-
sent: acc.sent,
|
|
313
|
-
persisted: acc.persisted,
|
|
314
|
-
dropped: Math.max(0, acc.sent - acc.persisted),
|
|
315
|
-
});
|
|
316
|
-
const settle = (reason: string): TelemetryFlushSummary =>
|
|
317
|
-
acc.sent > 0 ? flushed() : { flushed: false, reason };
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
if (batchCount >= MAX_CONSECUTIVE_BATCHES) {
|
|
321
|
-
return flushed();
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
// Age-bound the ack-mode outbox before any skip gate (platform,
|
|
325
|
-
// checkpoint store, consent three-way), so the bound holds in every
|
|
326
|
-
// state — including the unknown-consent deferral below. Only the
|
|
327
|
-
// reporter partition that flushes the outbox (ack-mode sources)
|
|
328
|
-
// prunes it.
|
|
329
|
-
if (batchCount === 0 && this.sources.some((source) => source.ack)) {
|
|
330
|
-
const prunedCount = deleteTelemetryOutboxEventsBefore(
|
|
331
|
-
Date.now() - OUTBOX_MAX_ROW_AGE_MS,
|
|
332
|
-
);
|
|
333
|
-
if (prunedCount > 0) {
|
|
334
|
-
log.debug(
|
|
335
|
-
{ prunedCount },
|
|
336
|
-
"Telemetry flush: pruned expired outbox rows",
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Skip when platform features are disabled (CADET_DISABLE_CLOUD in
|
|
342
|
-
// local mode; the flag is ignored when IS_CLOUD is set, matching
|
|
343
|
-
// CadetCloudClient.create()). Watermarks are NOT advanced here: this
|
|
344
|
-
// is a deployment/local-mode toggle, not a privacy opt-out, so the unsent
|
|
345
|
-
// backlog ships once the flag is cleared.
|
|
346
|
-
if (!areCloudFeaturesEnabled()) {
|
|
347
|
-
return settle("disabled");
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Skip when the flush-checkpoint store (telemetry DB) is unreachable.
|
|
351
|
-
// An unreadable watermark must not be mistaken for cursor 0 — that
|
|
352
|
-
// would requery and re-ship history from the beginning. Nothing is
|
|
353
|
-
// advanced or sent; the cycle retries once the store is back.
|
|
354
|
-
if (!this.checkpoints.isAvailable()) {
|
|
355
|
-
log.warn(
|
|
356
|
-
"Telemetry flush: flush-checkpoint store unavailable — skipping, will retry next cycle",
|
|
357
|
-
);
|
|
358
|
-
return settle("checkpoint-unavailable");
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
// Tri-state consent gate: `true` ships, `false` purges, `"unknown"`
|
|
362
|
-
// defers.
|
|
363
|
-
const shareAnalytics = getRawShareAnalytics();
|
|
364
|
-
|
|
365
|
-
// `"unknown"` (boot before the first successful consent refresh, no
|
|
366
|
-
// platform session, no resolvable owner) is not an opt-out: nothing is
|
|
367
|
-
// sent, acked, discarded, or advanced — the same shape as the
|
|
368
|
-
// checkpoint-unavailable skip above. Purging here would silently
|
|
369
|
-
// destroy events recorded before the cache warmed up; instead the
|
|
370
|
-
// backlog ships (or is purged) on a later cycle once the 5-minute
|
|
371
|
-
// refresh loop resolves the cache to a confirmed value. Bounded by
|
|
372
|
-
// the age prune above.
|
|
373
|
-
if (shareAnalytics === "unknown") {
|
|
374
|
-
log.debug(
|
|
375
|
-
"Telemetry flush: share_analytics consent unknown — skipping, will retry next cycle",
|
|
376
|
-
);
|
|
377
|
-
return settle("unknown-consent");
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Respect a confirmed opt-out: if the cloud owner declined
|
|
381
|
-
// `share_analytics` consent, skip the flush and advance watermarks so
|
|
382
|
-
// events recorded during the opt-out window are never sent
|
|
383
|
-
// retroactively. The daemon runs the reporter even when opted out
|
|
384
|
-
// specifically so this branch keeps executing — every cycle plus the
|
|
385
|
-
// final flush in stop() — which is what lets a later opt-in (runtime or
|
|
386
|
-
// via restart) resume from a watermark that already covers the opt-out
|
|
387
|
-
// window. One caveat: a RUNTIME false→true flip can still ship up to one
|
|
388
|
-
// flush interval (≤5 min) of pre-toggle rows recorded since the last
|
|
389
|
-
// opted-out flush;
|
|
390
|
-
// the restart path is fully covered by the final flush in stop(). The
|
|
391
|
-
// caveat applies to the always-on tables without a write-time opt-out
|
|
392
|
-
// gate (llm_usage, turn events) and to tool_invocations rows recorded
|
|
393
|
-
// under builds predating the audit listener's write-time gate — new
|
|
394
|
-
// opted-out tool_invocations rows persist NULL telemetry columns and
|
|
395
|
-
// are unreportable by construction regardless of watermark timing.
|
|
396
|
-
if (shareAnalytics === false) {
|
|
397
|
-
// Ack-mode sources drop their pending rows outright. Watermark-mode
|
|
398
|
-
// sources advance the timestamp watermarks and pin the ID watermarks
|
|
399
|
-
// to a sentinel that sorts above any real UUID. The sentinel (rather
|
|
400
|
-
// than "") keeps the compound-cursor branch active — a falsy ID would
|
|
401
|
-
// downgrade the query to a timestamp-only `gt(createdAt, watermark)`
|
|
402
|
-
// — while making its same-millisecond arm unsatisfiable, so a row
|
|
403
|
-
// written in the same millisecond as this flush's Date.now() can
|
|
404
|
-
// never ship after a later opt-in. The next opted-in flush that
|
|
405
|
-
// ships events overwrites the sentinel with a real row ID.
|
|
406
|
-
const now = String(Date.now());
|
|
407
|
-
for (const source of this.sources) {
|
|
408
|
-
if (source.ack) {
|
|
409
|
-
source.ack.discardPending();
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
const keys = watermarkKeysForSource(source.id);
|
|
413
|
-
this.checkpoints.set(keys.at, now);
|
|
414
|
-
this.checkpoints.set(keys.id, OPT_OUT_WATERMARK_ID_SENTINEL);
|
|
415
|
-
}
|
|
416
|
-
return settle("opted-out");
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Read each source's watermark (compound cursor: createdAt + id) and
|
|
420
|
-
// collect its reportable batch. Absent checkpoints default to cursor 0
|
|
421
|
-
// — safe for the consent-gated tables (opted-out rows never exist) and
|
|
422
|
-
// for tool_executed, whose absent watermark was initialized at
|
|
423
|
-
// construction (see the constructor).
|
|
424
|
-
const batches: Array<{
|
|
425
|
-
source: TelemetryEventSource;
|
|
426
|
-
batch: TelemetryEventSourceBatch;
|
|
427
|
-
}> = this.sources.map((source) => {
|
|
428
|
-
const keys = watermarkKeysForSource(source.id);
|
|
429
|
-
const afterCreatedAt = Number(this.checkpoints.get(keys.at) ?? "0");
|
|
430
|
-
const afterId = this.checkpoints.get(keys.id) ?? undefined;
|
|
431
|
-
return {
|
|
432
|
-
source,
|
|
433
|
-
batch: source.collect(afterCreatedAt, afterId, BATCH_SIZE),
|
|
434
|
-
};
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
// Ack-mode batches must carry a row id per event: without them nothing
|
|
438
|
-
// can be acknowledged post-2xx, so shipping would re-send the same rows
|
|
439
|
-
// on every flush. Exclude such a batch from the POST instead.
|
|
440
|
-
for (const entry of batches) {
|
|
441
|
-
const { source, batch } = entry;
|
|
442
|
-
if (
|
|
443
|
-
source.ack &&
|
|
444
|
-
batch.events.length > 0 &&
|
|
445
|
-
(batch.rowIds ?? []).length !== batch.events.length
|
|
446
|
-
) {
|
|
447
|
-
log.warn(
|
|
448
|
-
{
|
|
449
|
-
sourceId: source.id,
|
|
450
|
-
eventCount: batch.events.length,
|
|
451
|
-
rowIdCount: batch.rowIds?.length ?? 0,
|
|
452
|
-
},
|
|
453
|
-
"Telemetry flush: ack-mode batch missing/mismatched rowIds — excluding from POST",
|
|
454
|
-
);
|
|
455
|
-
entry.batch = {
|
|
456
|
-
events: [],
|
|
457
|
-
rowIds: [],
|
|
458
|
-
lastCursor: null,
|
|
459
|
-
fullBatch: false,
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (batches.every(({ batch }) => batch.events.length === 0)) {
|
|
465
|
-
return settle("nothing-pending");
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// Resolve auth context. We send authenticated-only: if no platform
|
|
469
|
-
// credentials are available yet, skip without advancing watermarks so the
|
|
470
|
-
// backlog ships on a later cycle once credentials resolve.
|
|
471
|
-
const client = await CadetCloudClient.create();
|
|
472
|
-
if (!client) {
|
|
473
|
-
log.debug(
|
|
474
|
-
{
|
|
475
|
-
pendingEventCount: batches.reduce(
|
|
476
|
-
(total, { batch }) => total + batch.events.length,
|
|
477
|
-
0,
|
|
478
|
-
),
|
|
479
|
-
},
|
|
480
|
-
"Telemetry flush: no cloud credentials — skipping, will retry next cycle",
|
|
481
|
-
);
|
|
482
|
-
return settle("no-credentials");
|
|
483
|
-
}
|
|
484
|
-
log.debug(
|
|
485
|
-
{
|
|
486
|
-
counts: Object.fromEntries(
|
|
487
|
-
batches.map(({ source, batch }) => [
|
|
488
|
-
source.id,
|
|
489
|
-
batch.events.length,
|
|
490
|
-
]),
|
|
491
|
-
),
|
|
492
|
-
},
|
|
493
|
-
"Telemetry flush: resolved auth context",
|
|
494
|
-
);
|
|
495
|
-
|
|
496
|
-
// Build payload — sources in construction order, each source's events
|
|
497
|
-
// in cursor order.
|
|
498
|
-
const typedEvents = batches.flatMap(({ batch }) => batch.events);
|
|
499
|
-
|
|
500
|
-
// Pre-flush wire validation — observability only: warns about events
|
|
501
|
-
// the server would silently drop; the batch is POSTed unchanged.
|
|
502
|
-
validateWireEvents(typedEvents, log);
|
|
503
|
-
|
|
504
|
-
const organizationId = getCloudOrganizationId() || undefined;
|
|
505
|
-
const userId = getCloudUserId() || undefined;
|
|
506
|
-
const payload = {
|
|
507
|
-
device_id: getDeviceId(),
|
|
508
|
-
assistant_version: APP_VERSION,
|
|
509
|
-
...(organizationId ? { organization_id: organizationId } : {}),
|
|
510
|
-
...(userId ? { user_id: userId } : {}),
|
|
511
|
-
events: typedEvents,
|
|
512
|
-
};
|
|
513
|
-
|
|
514
|
-
// Send
|
|
515
|
-
const fetchInit: RequestInit = {
|
|
516
|
-
method: "POST",
|
|
517
|
-
headers: {
|
|
518
|
-
"Content-Type": "application/json",
|
|
519
|
-
},
|
|
520
|
-
body: JSON.stringify(payload),
|
|
521
|
-
};
|
|
522
|
-
|
|
523
|
-
const resp = await client.fetch(TELEMETRY_PATH, fetchInit);
|
|
524
|
-
|
|
525
|
-
if (!resp.ok) {
|
|
526
|
-
const body = await resp.text();
|
|
527
|
-
log.warn(
|
|
528
|
-
{ status: resp.status, body },
|
|
529
|
-
"Usage telemetry POST failed — will retry next cycle",
|
|
530
|
-
);
|
|
531
|
-
return settle("post-failed");
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
// Read the ingest response so `persisted` reflects what actually landed;
|
|
535
|
-
// consuming the body also releases the connection.
|
|
536
|
-
const body = await resp.text();
|
|
537
|
-
acc.sent += typedEvents.length;
|
|
538
|
-
acc.persisted += parsePersistedCount(body, typedEvents.length);
|
|
539
|
-
|
|
540
|
-
// Acknowledge each source's shipped rows: ack-mode sources delete them
|
|
541
|
-
// (never touching flush_checkpoints), watermark-mode sources advance
|
|
542
|
-
// their compound cursor to the last reported row.
|
|
543
|
-
for (const { source, batch } of batches) {
|
|
544
|
-
if (source.ack) {
|
|
545
|
-
if (batch.events.length > 0) {
|
|
546
|
-
source.ack.acknowledge(batch.rowIds ?? []);
|
|
547
|
-
}
|
|
548
|
-
} else if (batch.lastCursor) {
|
|
549
|
-
const keys = watermarkKeysForSource(source.id);
|
|
550
|
-
this.checkpoints.set(keys.at, String(batch.lastCursor.createdAt));
|
|
551
|
-
this.checkpoints.set(keys.id, batch.lastCursor.id);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// If any source produced a full batch, there may be more — recurse,
|
|
556
|
-
// threading the accumulator so counts span every batch in this flush.
|
|
557
|
-
if (batches.some(({ batch }) => batch.fullBatch)) {
|
|
558
|
-
return this._doFlush(batchCount + 1, acc);
|
|
559
|
-
}
|
|
560
|
-
return flushed();
|
|
561
|
-
} catch (err) {
|
|
562
|
-
log.warn({ err }, "Usage telemetry flush error — non-fatal, will retry");
|
|
563
|
-
return settle("error");
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for the direct (unbuffered) watchdog telemetry emit.
|
|
3
|
-
*
|
|
4
|
-
* Unlike the batched reporter, this POSTs a single event straight to the
|
|
5
|
-
* platform ingest. It must honor the `share_analytics` opt-out and the
|
|
6
|
-
* platform-features gate, skip cleanly when credentials aren't resolved yet,
|
|
7
|
-
* and otherwise send a well-formed single-event payload.
|
|
8
|
-
*/
|
|
9
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
|
-
|
|
11
|
-
import type { ConsentState } from "../cloud/consent-cache.js";
|
|
12
|
-
|
|
13
|
-
let shareAnalytics: ConsentState = true;
|
|
14
|
-
mock.module("../cloud/consent-cache.js", () => ({
|
|
15
|
-
getRawShareAnalytics: () => shareAnalytics,
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
let platformEnabled = true;
|
|
19
|
-
mock.module("../cloud/feature-gate.js", () => ({
|
|
20
|
-
areCloudFeaturesEnabled: () => platformEnabled,
|
|
21
|
-
}));
|
|
22
|
-
|
|
23
|
-
const fetchCalls: Array<{ path: string; body: string }> = [];
|
|
24
|
-
let clientAvailable = true;
|
|
25
|
-
mock.module("../cloud/client.js", () => ({
|
|
26
|
-
CadetCloudClient: {
|
|
27
|
-
create: async () =>
|
|
28
|
-
clientAvailable
|
|
29
|
-
? {
|
|
30
|
-
fetch: async (path: string, init: { body: string }) => {
|
|
31
|
-
fetchCalls.push({ path, body: init.body });
|
|
32
|
-
return new Response("", { status: 200 });
|
|
33
|
-
},
|
|
34
|
-
}
|
|
35
|
-
: null,
|
|
36
|
-
},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
import { setCloudOrganizationId, setCloudUserId } from "../config/env.js";
|
|
40
|
-
setCloudOrganizationId("org-1");
|
|
41
|
-
setCloudUserId("user-1");
|
|
42
|
-
|
|
43
|
-
mock.module("../util/device-id.js", () => ({
|
|
44
|
-
getDeviceId: () => "device-xyz",
|
|
45
|
-
}));
|
|
46
|
-
mock.module("../version.js", () => ({ APP_VERSION: "9.9.9" }));
|
|
47
|
-
|
|
48
|
-
import { emitWatchdogEventDirect } from "./watchdog-direct-emit.js";
|
|
49
|
-
|
|
50
|
-
describe("emitWatchdogEventDirect", () => {
|
|
51
|
-
beforeEach(() => {
|
|
52
|
-
fetchCalls.length = 0;
|
|
53
|
-
shareAnalytics = true;
|
|
54
|
-
platformEnabled = true;
|
|
55
|
-
clientAvailable = true;
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("POSTs a single well-formed watchdog event when opted in", async () => {
|
|
59
|
-
await emitWatchdogEventDirect("sqlite_corrupted", {
|
|
60
|
-
database: "main",
|
|
61
|
-
error: "database disk image is malformed",
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(fetchCalls).toHaveLength(1);
|
|
65
|
-
expect(fetchCalls[0].path).toBe("/v1/telemetry/ingest/");
|
|
66
|
-
const payload = JSON.parse(fetchCalls[0].body) as {
|
|
67
|
-
device_id: string;
|
|
68
|
-
assistant_version: string;
|
|
69
|
-
organization_id?: string;
|
|
70
|
-
user_id?: string;
|
|
71
|
-
events: Array<Record<string, unknown>>;
|
|
72
|
-
};
|
|
73
|
-
expect(payload.device_id).toBe("device-xyz");
|
|
74
|
-
expect(payload.assistant_version).toBe("9.9.9");
|
|
75
|
-
expect(payload.organization_id).toBe("org-1");
|
|
76
|
-
expect(payload.user_id).toBe("user-1");
|
|
77
|
-
expect(payload.events).toHaveLength(1);
|
|
78
|
-
const event = payload.events[0];
|
|
79
|
-
expect(event.type).toBe("watchdog");
|
|
80
|
-
expect(event.check_name).toBe("sqlite_corrupted");
|
|
81
|
-
expect(event.value).toBeNull();
|
|
82
|
-
expect(event.daemon_event_id).toBeString();
|
|
83
|
-
expect(event.detail).toMatchObject({
|
|
84
|
-
database: "main",
|
|
85
|
-
error: "database disk image is malformed",
|
|
86
|
-
});
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test("sends nothing when share_analytics is a confirmed opt-out", async () => {
|
|
90
|
-
shareAnalytics = false;
|
|
91
|
-
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
92
|
-
expect(fetchCalls).toHaveLength(0);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test("emits while consent is unknown (no buffer to defer into)", async () => {
|
|
96
|
-
shareAnalytics = "unknown";
|
|
97
|
-
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
98
|
-
expect(fetchCalls).toHaveLength(1);
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
test("sends nothing when platform features are disabled", async () => {
|
|
102
|
-
platformEnabled = false;
|
|
103
|
-
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
104
|
-
expect(fetchCalls).toHaveLength(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("sends nothing (no throw) when credentials aren't resolved yet", async () => {
|
|
108
|
-
clientAvailable = false;
|
|
109
|
-
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
110
|
-
expect(fetchCalls).toHaveLength(0);
|
|
111
|
-
});
|
|
112
|
-
});
|