session-orchestrator 3.23.0 → 4.0.0
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/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +13 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1401 -0
- package/NOTICE +11 -6
- package/README.md +127 -92
- package/agents/db-specialist.md +0 -1
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +249 -48
- package/docs/codex-setup.md +66 -22
- package/docs/components.md +37 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +51 -10
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +8 -8
- package/docs/session-config-reference.md +120 -61
- package/docs/session-config-template.md +40 -33
- package/docs/telemetry/telemetry-claims.md +11 -10
- package/docs/telemetry.md +187 -4
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/atomic-json.mjs +111 -0
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-paths.mjs +143 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +11 -31
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/enforce-scope.mjs +35 -6
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +280 -14
- package/hooks/on-session-start.mjs +153 -4
- package/hooks/on-stop.mjs +371 -17
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +189 -4
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +278 -392
- package/hooks/post-tool-batch-wave-signal.mjs +272 -44
- package/hooks/post-tool-failure-corrective-context.mjs +11 -34
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +24 -30
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/autopilot.mjs +26 -12
- package/scripts/backfill-abandoned-sessions.mjs +130 -15
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/emit-event.mjs +10 -2
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auq/parse.mjs +5 -29
- package/scripts/lib/auto-dialectic.mjs +68 -0
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
- package/scripts/lib/build-live-signals.mjs +25 -22
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/cold-start-detector.mjs +23 -14
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +63 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +9 -3
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +388 -0
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +234 -0
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/config.mjs +31 -3
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/enumerate.mjs +2 -17
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events-schema.mjs +48 -0
- package/scripts/lib/events.mjs +256 -7
- package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-banner.mjs +20 -8
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/peer-discovery.mjs +20 -2
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +249 -9
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/scope-gate.mjs +36 -0
- package/scripts/lib/session-close-backfill.mjs +125 -18
- package/scripts/lib/session-discovery.mjs +57 -3
- package/scripts/lib/session-end/phase-skip.mjs +2 -2
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +187 -11
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/session-transition.mjs +1 -1
- package/scripts/lib/sessions-canonical.mjs +446 -0
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +255 -17
- package/scripts/lib/telemetry/sync.mjs +417 -24
- package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +455 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
- package/scripts/lib/validate/check-unwired-features.mjs +0 -9
- package/scripts/lib/validate/check-validator-registration.mjs +254 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/markdown-fences.mjs +196 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/board-lock.mjs +185 -0
- package/scripts/lib/vault-status/board-writer.mjs +174 -135
- package/scripts/lib/vault-status/narrative-mirror.mjs +129 -37
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +502 -0
- package/scripts/lib/wave-resource-gate.mjs +133 -7
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +142 -8
- package/scripts/materialize-wave-scope.mjs +32 -9
- package/scripts/memory-propose.mjs +146 -8
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/promote-vault-strict.mjs +4 -15
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +36 -4
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +187 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/vault-consolidate.mjs +3 -11
- package/scripts/vault-integration-watcher.mjs +2 -4
- package/scripts/vault-mirror.mjs +111 -26
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-auq.md +31 -2
- package/skills/_shared/parallel-aware-preamble.md +18 -4
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/ecosystem-health/SKILL.md +4 -1
- package/skills/ecosystem-health/wizard.md +5 -0
- package/skills/evolve/SKILL.md +87 -11
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +38 -2
- package/skills/remote-offload/SKILL.md +89 -0
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +19 -9
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +41 -900
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +16 -2
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1271
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -269
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* none and holds no mutable runtime state.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
-
import {
|
|
38
|
+
import { getPlatform, getPluginRoot } from '../platform.mjs';
|
|
39
39
|
import { enumerateSurface } from '../sunset/walker.mjs';
|
|
40
40
|
import { readPluginVersionFromPackageJson } from '../bootstrap-lock-freshness.mjs';
|
|
41
41
|
|
|
@@ -70,6 +70,52 @@ export const USAGE_PING_FIELDS = Object.freeze([
|
|
|
70
70
|
'commands',
|
|
71
71
|
]);
|
|
72
72
|
|
|
73
|
+
/**
|
|
74
|
+
* ADDITIVE optional fields (schema v1, GitLab #1234). Deliberately a SECOND
|
|
75
|
+
* list rather than an extension of USAGE_PING_FIELDS above, for two reasons:
|
|
76
|
+
*
|
|
77
|
+
* 1. USAGE_PING_FIELDS is the REQUIRED v1 contract — `tests/telemetry/parity.mjs`
|
|
78
|
+
* asserts, field by field, that the server independently REQUIRES every
|
|
79
|
+
* member. An optional field is by definition not required, so putting it in
|
|
80
|
+
* that list would force the parity guard to be weakened for all 15 fields to
|
|
81
|
+
* accommodate one.
|
|
82
|
+
* 2. The frozen-list tripwire (`projectUsagePing` drops anything unlisted)
|
|
83
|
+
* stays intact: the projection whitelist is the UNION of both lists, so a
|
|
84
|
+
* leaky field still has to be added to a reviewed list before it can reach
|
|
85
|
+
* the wire.
|
|
86
|
+
*
|
|
87
|
+
* Server tolerance: `server/ingest/validate.mjs` accepts unknown top-level
|
|
88
|
+
* fields and preserves them verbatim in `raw_json` (see its module docblock), so
|
|
89
|
+
* every member here round-trips through a server that predates it.
|
|
90
|
+
*/
|
|
91
|
+
export const USAGE_PING_OPTIONAL_FIELDS = Object.freeze([
|
|
92
|
+
'fleet_self_declared',
|
|
93
|
+
'session_record',
|
|
94
|
+
'session_profile',
|
|
95
|
+
]);
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The full projection whitelist: required v1 fields + additive optional fields.
|
|
99
|
+
* `projectUsagePing` is driven from THIS, so neither list alone can leak a field.
|
|
100
|
+
*/
|
|
101
|
+
const USAGE_PING_PROJECTED_FIELDS = Object.freeze([...USAGE_PING_FIELDS, ...USAGE_PING_OPTIONAL_FIELDS]);
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* `session_record` provenance tokens — WHICH source the session facts
|
|
105
|
+
* (`session_type`, `duration_bucket`) in this ping came from.
|
|
106
|
+
*
|
|
107
|
+
* - `ledger` — `.orchestrator/metrics/sessions.jsonl` had a matching record.
|
|
108
|
+
* - `derived` — no ledger record; the facts were reconstructed from
|
|
109
|
+
* `events.jsonl` (`orchestrator.session.started` + last event).
|
|
110
|
+
* - `absent` — neither source produced a session type. `session_type` is then
|
|
111
|
+
* `'unknown'` and `duration_bucket` is NOT a measurement.
|
|
112
|
+
*
|
|
113
|
+
* Measured 2026-09-06 (d8 audit): this repo has NO sessions.jsonl, so 100 % of
|
|
114
|
+
* its pings took the `absent` path and were indistinguishable on the wire from
|
|
115
|
+
* a genuinely-measured `other` / `<15m` session — 32 such pings on the server.
|
|
116
|
+
*/
|
|
117
|
+
export const SESSION_RECORD_SOURCES = Object.freeze(['ledger', 'derived', 'absent']);
|
|
118
|
+
|
|
73
119
|
/** Exact duration-bucket tokens (ASCII, stable wire values). */
|
|
74
120
|
export const DURATION_BUCKETS = Object.freeze(['<15m', '15-60m', '1-3h', '>3h']);
|
|
75
121
|
|
|
@@ -83,6 +129,43 @@ const VALID_PLATFORMS = Object.freeze(['claude', 'codex', 'cursor', 'pi']);
|
|
|
83
129
|
const VALID_SESSION_TYPES = Object.freeze(['housekeeping', 'feature', 'deep']);
|
|
84
130
|
const PLATFORM_OTHER = 'other';
|
|
85
131
|
const SESSION_TYPE_OTHER = 'other';
|
|
132
|
+
/**
|
|
133
|
+
* The type was never measured — distinct from `'other'`, which means "measured,
|
|
134
|
+
* but not one of the three known modes". Conflating the two is the defect this
|
|
135
|
+
* token fixes: before it existed, a ping built with NO session record silently
|
|
136
|
+
* reported `'other'`, so "we could not tell" and "we looked and it was unusual"
|
|
137
|
+
* were the same wire value.
|
|
138
|
+
*
|
|
139
|
+
* NOT an enum widening server-side: `session_type` is validated as a bounded
|
|
140
|
+
* STRING (`server/ingest/validate.mjs` `requireString` + `MAX_SESSION_TYPE`),
|
|
141
|
+
* never against a closed set, so `'unknown'` is accepted by today's server.
|
|
142
|
+
*/
|
|
143
|
+
const SESSION_TYPE_UNKNOWN = 'unknown';
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* CLOSED whitelist of PUBLIC session-profile names that may reach the wire.
|
|
147
|
+
*
|
|
148
|
+
* WHY A WHITELIST AND NOT A REGEX: `session_profile` is copied from the STATE.md
|
|
149
|
+
* frontmatter key `session-profile`, which is written per repo by whoever runs
|
|
150
|
+
* `/session <alias>` — i.e. it is the only field on the usage-ping whose VALUE is
|
|
151
|
+
* repo-authored free text. Two independent Wave-1 reviewers reproduced the leak
|
|
152
|
+
* end-to-end (2026-09-06): `session-profile: client-acme-private-repo` travelled
|
|
153
|
+
* verbatim through `buildUsagePing` → `projectUsagePing` → the ingest server's
|
|
154
|
+
* `raw_json` column. A shape regex does NOT close it — that example string is
|
|
155
|
+
* already lowercase-and-hyphens and passes any such pattern. Only an
|
|
156
|
+
* enumeration of names that are public BY CONSTRUCTION does.
|
|
157
|
+
*
|
|
158
|
+
* Today exactly one profile exists: `ultradeep`, the 7-wave variant of a `deep`
|
|
159
|
+
* session (writer: `commands/session.md` § "Argument alias: ultradeep" → STATE.md
|
|
160
|
+
* frontmatter `session-profile`; wave shape: `skills/session-plan/SKILL.md`).
|
|
161
|
+
* Adding a profile there means adding it HERE and in the server's mirror
|
|
162
|
+
* (`server/ingest/validate.mjs` `SESSION_PROFILES`) — a deliberate two-line,
|
|
163
|
+
* reviewed edit, the same contract `ACCEPTED_VERSIONS` already uses server-side.
|
|
164
|
+
*
|
|
165
|
+
* NOT a member of VALID_SESSION_TYPES: the profile is a SECOND axis. An
|
|
166
|
+
* ultradeep session is `session_type: "deep"` PLUS `session_profile: "ultradeep"`.
|
|
167
|
+
*/
|
|
168
|
+
export const VALID_SESSION_PROFILES = Object.freeze(['ultradeep']);
|
|
86
169
|
|
|
87
170
|
/**
|
|
88
171
|
* Closed sets for os/arch client-side normalization. A value outside the set —
|
|
@@ -121,7 +204,8 @@ function isNonEmptyString(v) {
|
|
|
121
204
|
// ---------------------------------------------------------------------------
|
|
122
205
|
|
|
123
206
|
/**
|
|
124
|
-
* Project an arbitrary object onto the usage-ping whitelist (USAGE_PING_FIELDS
|
|
207
|
+
* Project an arbitrary object onto the usage-ping whitelist (USAGE_PING_FIELDS
|
|
208
|
+
* PLUS USAGE_PING_OPTIONAL_FIELDS).
|
|
125
209
|
* Fully data-driven: any key not on the whitelist — paths, repo names, prompts,
|
|
126
210
|
* args, hostnames, rogue extras — is dropped. Array fields (skills, commands) are
|
|
127
211
|
* copied as NEW arrays so no caller reference leaks into the projection.
|
|
@@ -132,7 +216,7 @@ function isNonEmptyString(v) {
|
|
|
132
216
|
export function projectUsagePing(input) {
|
|
133
217
|
if (!isPlainObject(input)) return {};
|
|
134
218
|
const out = {};
|
|
135
|
-
for (const key of
|
|
219
|
+
for (const key of USAGE_PING_PROJECTED_FIELDS) {
|
|
136
220
|
if (key in input) {
|
|
137
221
|
const v = input[key];
|
|
138
222
|
out[key] = Array.isArray(v) ? [...v] : v;
|
|
@@ -163,7 +247,7 @@ export function projectUsagePing(input) {
|
|
|
163
247
|
export function loadRoster({ pluginRoot } = {}) {
|
|
164
248
|
const root = (typeof pluginRoot === 'string' && pluginRoot.trim() !== '')
|
|
165
249
|
? pluginRoot
|
|
166
|
-
:
|
|
250
|
+
: getPluginRoot();
|
|
167
251
|
|
|
168
252
|
if (!isNonEmptyString(root)) {
|
|
169
253
|
process.stderr.write(
|
|
@@ -220,6 +304,54 @@ export function filterRosterNames(names, rosterSet) {
|
|
|
220
304
|
return deduped.slice(0, MAX_NAMES);
|
|
221
305
|
}
|
|
222
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Classify a single raw invocation name into the skills or commands bucket.
|
|
309
|
+
*
|
|
310
|
+
* The Skill tool surfaces slash-commands that have NO backing `skills/` directory
|
|
311
|
+
* under the same plugin-prefixed name form as real skills (e.g.
|
|
312
|
+
* `session-orchestrator:session` for `commands/session.md`). `roster.skills` holds
|
|
313
|
+
* PREFIXED names, `roster.commands` holds BARE ones — so a prefixed command name
|
|
314
|
+
* matches neither set and used to be bucketed to 'other' (GitLab #1189: commands
|
|
315
|
+
* was `[]` in 345/345 usage pings).
|
|
316
|
+
*
|
|
317
|
+
* Decision order (skill roster wins on a spelling collision such as
|
|
318
|
+
* `memory-cleanup`, which is both a skill dir and a command file):
|
|
319
|
+
* 1. name in rosterSkills → {kind:'skill', name} (verbatim)
|
|
320
|
+
* 2. name is PREFIXED and its bare form is in rosterCommands
|
|
321
|
+
* → {kind:'command', bare}
|
|
322
|
+
* 3. otherwise → {kind:'skill', name} (verbatim)
|
|
323
|
+
*
|
|
324
|
+
* Case 2 REQUIRES the plugin prefix (session-reviewer W2 finding, 2026-09-02).
|
|
325
|
+
* Without that requirement, a BARE third-party or personal skill name that
|
|
326
|
+
* happens to collide with one of our shipped command names (`test`, `close`,
|
|
327
|
+
* `go`, `release`, `portfolio` are all plausible foreign skill names) would be
|
|
328
|
+
* recorded as OUR command — a data-integrity defect, and the same rule made
|
|
329
|
+
* `memory-cleanup` reach BOTH buckets in one ping (bare → commands, prefixed →
|
|
330
|
+
* skills), double-counting a single surface. Requiring the prefix removes both:
|
|
331
|
+
* a bare arrival can only ever be a skill-path name, so an unknown one becomes
|
|
332
|
+
* 'other'. Cost: the one genuine bare `memory-cleanup` record is recorded as
|
|
333
|
+
* 'other' rather than as the command.
|
|
334
|
+
*
|
|
335
|
+
* Case 3 keeps foreign/third-party names on the SKILLS path only, where
|
|
336
|
+
* filterRosterNames projects them to the opaque token 'other' — an unclassified
|
|
337
|
+
* name is never duplicated into `commands`, preserving the privacy invariant.
|
|
338
|
+
*
|
|
339
|
+
* @param {unknown} name raw invocation name
|
|
340
|
+
* @param {Set<string>} rosterSkills prefixed shipped-skill names
|
|
341
|
+
* @param {Set<string>} rosterCommands bare shipped-command names
|
|
342
|
+
* @returns {{kind: 'skill'|'command', name: unknown}}
|
|
343
|
+
*/
|
|
344
|
+
export function classifyInvocationName(name, rosterSkills, rosterCommands) {
|
|
345
|
+
const skills = rosterSkills instanceof Set ? rosterSkills : new Set();
|
|
346
|
+
const commands = rosterCommands instanceof Set ? rosterCommands : new Set();
|
|
347
|
+
if (typeof name !== 'string') return { kind: 'skill', name };
|
|
348
|
+
if (skills.has(name)) return { kind: 'skill', name };
|
|
349
|
+
if (!name.startsWith(SKILL_PREFIX)) return { kind: 'skill', name };
|
|
350
|
+
const bare = name.slice(SKILL_PREFIX.length);
|
|
351
|
+
if (commands.has(bare)) return { kind: 'command', name: bare };
|
|
352
|
+
return { kind: 'skill', name };
|
|
353
|
+
}
|
|
354
|
+
|
|
223
355
|
// ---------------------------------------------------------------------------
|
|
224
356
|
// Duration bucketing
|
|
225
357
|
// ---------------------------------------------------------------------------
|
|
@@ -261,8 +393,9 @@ export function deriveDurationBucket(startedAtISO, completedAtISO) {
|
|
|
261
393
|
* whose null return (missing/unparseable package.json or non-string version) maps to 'unknown'.
|
|
262
394
|
*/
|
|
263
395
|
function resolvePluginVersion() {
|
|
264
|
-
|
|
265
|
-
|
|
396
|
+
const root = getPluginRoot();
|
|
397
|
+
if (!isNonEmptyString(root)) return 'unknown';
|
|
398
|
+
return readPluginVersionFromPackageJson(root) ?? 'unknown';
|
|
266
399
|
}
|
|
267
400
|
|
|
268
401
|
/** Normalize the detected platform to the closed enum (+ 'other' fallback). */
|
|
@@ -272,7 +405,36 @@ function normalizePlatform(platform) {
|
|
|
272
405
|
|
|
273
406
|
/** Normalize the session type to the closed enum (+ 'other' fallback). */
|
|
274
407
|
function normalizeSessionType(sessionType) {
|
|
275
|
-
|
|
408
|
+
if (VALID_SESSION_TYPES.includes(sessionType)) return sessionType;
|
|
409
|
+
// ABSENT ≠ UNRECOGNISED. A missing/blank value means nothing was measured
|
|
410
|
+
// (`unknown`); a present-but-unlisted value means something WAS measured and
|
|
411
|
+
// is not one of the three modes (`other`). The old single-branch version
|
|
412
|
+
// returned `other` for both, which is how 394 pings from a host with no
|
|
413
|
+
// sessions.jsonl arrived looking like measured `other` sessions (d8, 2026-09-06).
|
|
414
|
+
return isNonEmptyString(sessionType) ? SESSION_TYPE_OTHER : SESSION_TYPE_UNKNOWN;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Normalize a session PROFILE against the closed VALID_SESSION_PROFILES
|
|
419
|
+
* whitelist. Anything else — absent, blank, or an unlisted repo-authored value —
|
|
420
|
+
* returns `null`, and the caller OMITS the field.
|
|
421
|
+
*
|
|
422
|
+
* DELIBERATELY NOT MIRRORING normalizeSessionType's two-token report. That helper
|
|
423
|
+
* can distinguish ABSENT (`'unknown'`) from UNRECOGNISED (`'other'`) because both
|
|
424
|
+
* of its answers are public constants. Here the unrecognised VALUE is precisely
|
|
425
|
+
* what must not travel, and there is no third token that carries the distinction
|
|
426
|
+
* without carrying the string: an `'other'` profile would say "this host ran a
|
|
427
|
+
* profile we do not ship" — a fact of no product use, bought with a new wire
|
|
428
|
+
* value. So both cases collapse to omission, and the wire cannot tell them apart
|
|
429
|
+
* BY DESIGN. The local `session-profile` key is untouched either way.
|
|
430
|
+
*
|
|
431
|
+
* @param {unknown} profile
|
|
432
|
+
* @returns {string|null} a whitelisted profile name, or null (⇒ field omitted).
|
|
433
|
+
*/
|
|
434
|
+
export function normalizeSessionProfile(profile) {
|
|
435
|
+
if (!isNonEmptyString(profile)) return null;
|
|
436
|
+
const trimmed = profile.trim();
|
|
437
|
+
return VALID_SESSION_PROFILES.includes(trimmed) ? trimmed : null;
|
|
276
438
|
}
|
|
277
439
|
|
|
278
440
|
/**
|
|
@@ -331,12 +493,12 @@ function distinctField(records, field) {
|
|
|
331
493
|
* whitelist-clean but does NOT carry `anon_id` — the caller sets it via
|
|
332
494
|
* ensureAnonId (anon-id.mjs), keeping ID rotation isolated there.
|
|
333
495
|
*
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
* in without a signature change). Both are
|
|
338
|
-
* become "other" — deduped, sorted, and
|
|
339
|
-
* decision).
|
|
496
|
+
* The distinct `.skill` and `.command` values of `skillInvocations` go through a
|
|
497
|
+
* single classifyInvocationName pass that routes each name to skills or commands
|
|
498
|
+
* (the `.command` field carries nothing today; it is honored so a future direct
|
|
499
|
+
* command-telemetry stream feeds in without a signature change). Both buckets are
|
|
500
|
+
* then roster-filtered — off-roster names become "other" — deduped, sorted, and
|
|
501
|
+
* capped. No frequencies are recorded (v1 decision).
|
|
340
502
|
*
|
|
341
503
|
* @param {{
|
|
342
504
|
* sessionRecord: object,
|
|
@@ -355,6 +517,9 @@ export function buildUsagePing({
|
|
|
355
517
|
env = process.env,
|
|
356
518
|
now = new Date().toISOString(),
|
|
357
519
|
roster,
|
|
520
|
+
consentState,
|
|
521
|
+
sessionRecordSource,
|
|
522
|
+
sessionProfile,
|
|
358
523
|
} = {}) {
|
|
359
524
|
const session = isPlainObject(sessionRecord) ? sessionRecord : {};
|
|
360
525
|
const invocations = Array.isArray(skillInvocations) ? skillInvocations : [];
|
|
@@ -362,22 +527,95 @@ export function buildUsagePing({
|
|
|
362
527
|
const rosterSkills = rst?.skills instanceof Set ? rst.skills : new Set();
|
|
363
528
|
const rosterCommands = rst?.commands instanceof Set ? rst.commands : new Set();
|
|
364
529
|
|
|
365
|
-
|
|
366
|
-
|
|
530
|
+
// ONE classification pass over the union of both producer fields: today only
|
|
531
|
+
// `.skill` is written by hooks/skill-invocation-telemetry.mjs, `.command` is
|
|
532
|
+
// kept for the forward-compat producer documented above.
|
|
533
|
+
//
|
|
534
|
+
// `.command` records carry the BARE name, and classifyInvocationName requires
|
|
535
|
+
// the plugin prefix before it will route anything to the commands bucket (see
|
|
536
|
+
// its doc comment). So a `.command` name is normalized to the prefixed form
|
|
537
|
+
// first — the field itself is the "this is one of ours" signal that a bare
|
|
538
|
+
// `.skill` arrival lacks. Without this the forward-compat producer would be
|
|
539
|
+
// wired but dead: every record it writes would silently become 'other'.
|
|
540
|
+
const skillNames = [];
|
|
541
|
+
const commandNames = [];
|
|
542
|
+
const rawNames = [
|
|
543
|
+
...distinctField(invocations, 'skill'),
|
|
544
|
+
...distinctField(invocations, 'command').map((n) => (n.startsWith(SKILL_PREFIX) ? n : `${SKILL_PREFIX}${n}`)),
|
|
545
|
+
];
|
|
546
|
+
for (const raw of rawNames) {
|
|
547
|
+
const { kind, name } = classifyInvocationName(raw, rosterSkills, rosterCommands);
|
|
548
|
+
(kind === 'command' ? commandNames : skillNames).push(name);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// ── fleet: a statement about the OPERATOR, derived from the RESOLVED consent
|
|
552
|
+
// state, not from a raw owner.yaml read ────────────────────────────────────
|
|
553
|
+
// The old expression was `ownerConfig?.telemetry?.enabled === true` — a
|
|
554
|
+
// statement about a FILE. Measured 2026-09-06 (d8): the operator's second Mac
|
|
555
|
+
// has consent granted but no `telemetry:` block in owner.yaml, so 394 of 490
|
|
556
|
+
// server records (80,4 %) counted the operator as an external user and every
|
|
557
|
+
// week's `fleet_vs_external` was wrong.
|
|
558
|
+
//
|
|
559
|
+
// `resolveConsent()` already answers this question correctly: it returns
|
|
560
|
+
// `enabled-fleet` for the owner.yaml opt-in AND `enabled-env` for
|
|
561
|
+
// `SO_TELEMETRY=1`, both of which are operator-side postures. Callers that
|
|
562
|
+
// pass `consentState` get that answer; the ownerConfig fallback below keeps
|
|
563
|
+
// the two-argument callers (and the CLI preview) behaviourally identical.
|
|
564
|
+
//
|
|
565
|
+
// CEILING, named because the client cannot close it: this is still
|
|
566
|
+
// SELF-DECLARED — a sandbox or a host whose owner.yaml is unreachable declares
|
|
567
|
+
// `false` however honest it is. The authoritative classification is
|
|
568
|
+
// server-side (`SO_INGEST_FLEET_ANON_IDS`, server/ingest/config.mjs).
|
|
569
|
+
// `session_profile` (STATE.md frontmatter `session-profile`) is bounded by the
|
|
570
|
+
// CLOSED VALID_SESSION_PROFILES whitelist — it was the one free-text value on
|
|
571
|
+
// the wire until 2026-09-06, and a repo-authored one at that (see the
|
|
572
|
+
// whitelist's own docblock for the reproduced leak). It is still deliberately
|
|
573
|
+
// NOT routed through normalizeSessionType: that helper degrades anything
|
|
574
|
+
// outside ['housekeeping','feature','deep'] to 'other', which is exactly the
|
|
575
|
+
// silent loss the profile exists to prevent. The contract is
|
|
576
|
+
// `session_type: "deep"` PLUS `session_profile: "ultradeep"` — never
|
|
577
|
+
// `session_type: "ultradeep"`, and never a profile flattened to 'other'.
|
|
578
|
+
//
|
|
579
|
+
// ABSENT IS NOT EMPTY: with no profile — and with an UNLISTED one — the KEY IS
|
|
580
|
+
// OMITTED, matching every other optional ping field. `projectUsagePing` copies
|
|
581
|
+
// only keys that are `in` the input, so an omitted key never reaches the wire
|
|
582
|
+
// as `null`, and an unlisted profile never reaches it at all.
|
|
583
|
+
//
|
|
584
|
+
// SOURCE PRECEDENCE is resolved BEFORE normalization (explicit argument beats
|
|
585
|
+
// the session record), so an unlisted explicit profile is dropped rather than
|
|
586
|
+
// silently replaced by the record's value.
|
|
587
|
+
const rawProfile =
|
|
588
|
+
isNonEmptyString(sessionProfile) ? sessionProfile
|
|
589
|
+
: isNonEmptyString(session.session_profile) ? session.session_profile
|
|
590
|
+
: null;
|
|
591
|
+
const profile = normalizeSessionProfile(rawProfile);
|
|
592
|
+
|
|
593
|
+
const fleetSelfDeclared =
|
|
594
|
+
consentState === undefined || consentState === null
|
|
595
|
+
? ownerConfig?.telemetry?.enabled === true
|
|
596
|
+
: consentState === 'enabled-fleet' || consentState === 'enabled-env';
|
|
367
597
|
|
|
368
598
|
return {
|
|
369
599
|
record_kind: 'usage-ping',
|
|
370
600
|
schema_version: USAGE_PING_SCHEMA_VERSION,
|
|
371
601
|
sent_at: now,
|
|
372
602
|
plugin_version: resolvePluginVersion(),
|
|
373
|
-
platform: normalizePlatform(
|
|
603
|
+
platform: normalizePlatform(getPlatform()),
|
|
374
604
|
os: normalizeOs(process.platform),
|
|
375
605
|
arch: normalizeArch(process.arch),
|
|
376
606
|
node_major: parseInt(process.versions.node, 10),
|
|
377
607
|
ci: deriveCi(env),
|
|
378
|
-
fleet:
|
|
608
|
+
fleet: fleetSelfDeclared,
|
|
609
|
+
// DEPRECATED alias of `fleet_self_declared`, kept for one schema generation
|
|
610
|
+
// (removal: 2027-03-06 — see docs/telemetry.md § Schema evolution) so the
|
|
611
|
+
// server's existing `fleet` column stays comparable across the rename.
|
|
612
|
+
fleet_self_declared: fleetSelfDeclared,
|
|
379
613
|
session_type: normalizeSessionType(session.session_type),
|
|
614
|
+
...(profile !== null ? { session_profile: profile } : {}),
|
|
380
615
|
duration_bucket: deriveDurationBucket(session.started_at, session.completed_at),
|
|
616
|
+
session_record: SESSION_RECORD_SOURCES.includes(sessionRecordSource)
|
|
617
|
+
? sessionRecordSource
|
|
618
|
+
: 'absent',
|
|
381
619
|
skills: filterRosterNames(skillNames, rosterSkills),
|
|
382
620
|
commands: filterRosterNames(commandNames, rosterCommands),
|
|
383
621
|
};
|