@askexenow/exe-os 0.9.294 → 0.9.296
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/deploy/compose/cloudflared/config.yml.example +14 -9
- package/deploy/compose/docker-compose.yml +86 -8
- package/deploy/compose/sso-edge/default.conf.template +87 -0
- package/deploy/compose/sso-edge/entrypoint.sh +23 -0
- package/deploy/compose/sso-edge/sso-redirect.conf +63 -0
- package/deploy/stack-manifests/v0.9.json +2 -2
- package/dist/active-agent-AFX2FODG.js +28 -0
- package/dist/active-agent-E2IJA7YX.js +27 -0
- package/dist/agentic-ontology-A2YUZK5O.js +25 -0
- package/dist/assets/com.askexe.exed.plist +4 -1
- package/dist/backfill-metadata-OC7EOD5U.js +600 -0
- package/dist/behaviors-H5ZOVHDH.js +46 -0
- package/dist/bin/agentic-ontology-backfill.js +5 -5
- package/dist/bin/agentic-reflection-backfill.js +6 -6
- package/dist/bin/agentic-semantic-label.js +5 -5
- package/dist/bin/backfill-conversations.js +6 -6
- package/dist/bin/backfill-responses.js +6 -6
- package/dist/bin/backfill-vectors.js +8 -8
- package/dist/bin/bulk-sync-postgres.js +7 -7
- package/dist/bin/cc-doctor.js +4 -4
- package/dist/bin/cleanup-stale-review-tasks.js +11 -11
- package/dist/bin/cli.js +16 -16
- package/dist/bin/deferred-daemon-restart.js +1 -1
- package/dist/bin/exe-agent-config.js +2 -2
- package/dist/bin/exe-agent.js +4 -4
- package/dist/bin/exe-assign.js +8 -8
- package/dist/bin/exe-boot.js +21 -18
- package/dist/bin/exe-call.js +4 -4
- package/dist/bin/exe-cloud.js +7 -7
- package/dist/bin/exe-dispatch.js +11 -11
- package/dist/bin/exe-doctor.js +3 -2
- package/dist/bin/exe-export-behaviors.js +7 -7
- package/dist/bin/exe-forget.js +6 -6
- package/dist/bin/exe-gateway.js +7 -7
- package/dist/bin/exe-healthcheck.js +6 -4
- package/dist/bin/exe-heartbeat.js +11 -11
- package/dist/bin/exe-kill.js +14 -14
- package/dist/bin/exe-launch-agent.js +18 -18
- package/dist/bin/exe-new-employee.js +6 -6
- package/dist/bin/exe-pending-messages.js +12 -12
- package/dist/bin/exe-pending-notifications.js +11 -11
- package/dist/bin/exe-pending-reviews.js +11 -11
- package/dist/bin/exe-rename.js +4 -4
- package/dist/bin/exe-review.js +13 -13
- package/dist/bin/exe-search.js +5 -5
- package/dist/bin/exe-session-cleanup.js +16 -16
- package/dist/bin/exe-settings.js +39 -9
- package/dist/bin/exe-start-codex.js +11 -11
- package/dist/bin/exe-start-opencode.js +8 -8
- package/dist/bin/exe-status.js +12 -12
- package/dist/bin/exe-team.js +3 -3
- package/dist/bin/git-sweep.js +12 -12
- package/dist/bin/graph-backfill.js +4 -4
- package/dist/bin/graph-export.js +5 -5
- package/dist/bin/import-history.js +7 -7
- package/dist/bin/install-launchd.js +13 -6
- package/dist/bin/install.js +26 -14
- package/dist/bin/intercom-check.js +4 -4
- package/dist/bin/mcp-sessions.js +2 -2
- package/dist/bin/orchestration-metrics.js +4 -4
- package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
- package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
- package/dist/bin/scan-tasks.js +11 -11
- package/dist/bin/setup.js +1 -1
- package/dist/bin/shard-migrate.js +4 -4
- package/dist/bin/stack-update.js +2 -2
- package/dist/bin/vps-health-gate.js +1 -1
- package/dist/capability-cards-4USI7CUW.js +89 -0
- package/dist/capacity-monitor-WLCBTEYR.js +51 -0
- package/dist/catchup-brief-ZR3NX6LZ.js +175 -0
- package/dist/chunk-22TVSRQQ.js +226 -0
- package/dist/chunk-2E43UXRH.js +395 -0
- package/dist/chunk-2PIGT6UJ.js +460 -0
- package/dist/chunk-3XTMW2MZ.js +535 -0
- package/dist/chunk-465PQFTH.js +262 -0
- package/dist/chunk-5CCXU2AW.js +129 -0
- package/dist/chunk-5D6MPWR7.js +1094 -0
- package/dist/chunk-5Q4MR6SL.js +123 -0
- package/dist/chunk-6327RBWR.js +345 -0
- package/dist/chunk-6MZZREZY.js +199 -0
- package/dist/chunk-7DI2Q4O5.js +1186 -0
- package/dist/chunk-7PW5VNIY.js +122 -0
- package/dist/chunk-7T7Y56HW.js +43 -0
- package/dist/chunk-7UHCWCLT.js +128 -0
- package/dist/chunk-A2ZUMF6L.js +1350 -0
- package/dist/chunk-AKV44JEH.js +185 -0
- package/dist/chunk-ANHWGX5N.js +735 -0
- package/dist/chunk-BQ3P4TKD.js +97 -0
- package/dist/chunk-BUZMT3KZ.js +604 -0
- package/dist/chunk-C2SBESBO.js +210 -0
- package/dist/chunk-CLSXZUZW.js +51 -0
- package/dist/chunk-CONHLVAR.js +1079 -0
- package/dist/chunk-D3WTZPFX.js +456 -0
- package/dist/chunk-DE6SOIYL.js +197 -0
- package/dist/chunk-EIVNMA3Q.js +284 -0
- package/dist/chunk-EJIF4FNT.js +12 -0
- package/dist/chunk-FDFOW564.js +171 -0
- package/dist/chunk-GZUBJ5EC.js +127 -0
- package/dist/chunk-HGZITN22.js +105 -0
- package/dist/chunk-HSRKDU6X.js +362 -0
- package/dist/chunk-IIEN2PHV.js +85 -0
- package/dist/chunk-JQ56VLMM.js +567 -0
- package/dist/chunk-JVHHXRFY.js +280 -0
- package/dist/chunk-JXCXGZ3S.js +55 -0
- package/dist/chunk-K5ZO532Q.js +4388 -0
- package/dist/chunk-K6CAAMXF.js +97 -0
- package/dist/chunk-KA26YTNU.js +81 -0
- package/dist/chunk-KMUW5C3R.js +381 -0
- package/dist/chunk-KOO3J5PV.js +20 -0
- package/dist/chunk-LSV7OFIH.js +290 -0
- package/dist/chunk-LSVFDVNY.js +1158 -0
- package/dist/chunk-LXDQTW32.js +230 -0
- package/dist/chunk-MEP7OUVZ.js +181 -0
- package/dist/chunk-MN2B2LKS.js +240 -0
- package/dist/chunk-N2EAYPYQ.js +1352 -0
- package/dist/chunk-N7I2A667.js +70 -0
- package/dist/chunk-NLZHVIOP.js +630 -0
- package/dist/chunk-NUH5TRZL.js +227 -0
- package/dist/chunk-OAHEIH3G.js +167 -0
- package/dist/chunk-OBHRQGCK.js +58 -0
- package/dist/chunk-ODFA7B2V.js +54 -0
- package/dist/chunk-OSNUP45F.js +731 -0
- package/dist/chunk-OTPRHBTO.js +33 -0
- package/dist/chunk-P6MUA4QU.js +157 -0
- package/dist/chunk-PGIOFKSK.js +2093 -0
- package/dist/chunk-PSE7VHWK.js +50 -0
- package/dist/chunk-QIFUVZFW.js +331 -0
- package/dist/chunk-RDPXKTVK.js +221 -0
- package/dist/chunk-RKYTYJGB.js +76 -0
- package/dist/chunk-RXLR6EFM.js +348 -0
- package/dist/chunk-SDB67PQJ.js +159 -0
- package/dist/chunk-SF2T7MP3.js +402 -0
- package/dist/chunk-SLU3FRFQ.js +2133 -0
- package/dist/chunk-SNDZJ5IV.js +214 -0
- package/dist/chunk-STEEAABW.js +448 -0
- package/dist/chunk-TUTWNHIQ.js +244 -0
- package/dist/chunk-UDP35QBR.js +30 -0
- package/dist/chunk-UKFHNJBI.js +85 -0
- package/dist/chunk-VC2DTK2X.js +382 -0
- package/dist/chunk-VRRAE5JX.js +836 -0
- package/dist/chunk-VVJTBQPR.js +38 -0
- package/dist/chunk-W3EQ362K.js +581 -0
- package/dist/chunk-WHIXIFHC.js +2242 -0
- package/dist/chunk-WRNGJJNR.js +377 -0
- package/dist/chunk-WUKHLCBE.js +3313 -0
- package/dist/chunk-WVPLHGDG.js +150 -0
- package/dist/chunk-XJZBSTL5.js +204 -0
- package/dist/chunk-Y3PMNUM5.js +304 -0
- package/dist/chunk-YHVS4QOV.js +14597 -0
- package/dist/chunk-YJ2OYAOC.js +668 -0
- package/dist/chunk-YYAD2GXX.js +128 -0
- package/dist/chunk-ZQML7EWE.js +333 -0
- package/dist/co-activation-XJLH46OX.js +74 -0
- package/dist/co-occurrence-GNN2X526.js +95 -0
- package/dist/code-context-index-OCPRLFG5.js +30 -0
- package/dist/core-memory-J4W2IYOF.js +110 -0
- package/dist/crdt-sync-QCBTSHIH.js +33 -0
- package/dist/crm-webhook-EM442VUW.js +10 -0
- package/dist/cto-delegation-gate-MLJMVHBK.js +280 -0
- package/dist/daemon-orchestration-2VNLZVTW.js +139 -0
- package/dist/db-backup-VUGFTPJ4.js +43 -0
- package/dist/doc-graph-extractor-PNRSFPSS.js +133 -0
- package/dist/dreaming-SK5VEQRF.js +34 -0
- package/dist/entity-boost-TQWWJUC2.js +375 -0
- package/dist/exe-drift-N34UPO7S.js +70 -0
- package/dist/exe-export-KACBKGVV.js +77 -0
- package/dist/exe-import-GXGDWACG.js +80 -0
- package/dist/exe-key-XPDOZBWW.js +673 -0
- package/dist/exe-snapshot-32GQKGQ5.js +338 -0
- package/dist/fast-db-init-F3TDD5VV.js +7 -0
- package/dist/gateway/index.js +8 -8
- package/dist/git-staleness-J45WNYRF.js +112 -0
- package/dist/git-task-sweep-BTGVQPFB.js +42 -0
- package/dist/global-procedures-6JCQWU4D.js +22 -0
- package/dist/graph-auto-extract-3ZQNXTPC.js +183 -0
- package/dist/hooks/bug-report-worker.js +13 -13
- package/dist/hooks/codex-stop-task-finalizer.js +13 -13
- package/dist/hooks/commit-complete.js +13 -13
- package/dist/hooks/error-recall.js +6 -6
- package/dist/hooks/exe-heartbeat-hook.js +3 -3
- package/dist/hooks/ingest-worker.js +3 -3
- package/dist/hooks/ingest.js +6 -6
- package/dist/hooks/instructions-loaded.js +4 -4
- package/dist/hooks/manifest.json +20 -20
- package/dist/hooks/notification.js +4 -4
- package/dist/hooks/post-compact.js +12 -12
- package/dist/hooks/post-tool-combined.js +6 -6
- package/dist/hooks/pre-compact.js +16 -16
- package/dist/hooks/pre-tool-use.js +16 -16
- package/dist/hooks/prompt-submit.js +24 -24
- package/dist/hooks/session-end.js +21 -21
- package/dist/hooks/session-start.js +12 -12
- package/dist/hooks/stop.js +19 -19
- package/dist/hooks/subagent-stop.js +12 -12
- package/dist/hooks/summary-worker.js +19 -19
- package/dist/index.js +19 -19
- package/dist/installer-5VPFY7SB.js +298 -0
- package/dist/installer-OENFPMA2.js +344 -0
- package/dist/installer-OIX4QOG5.js +40 -0
- package/dist/lib/cloud-sync.js +7 -7
- package/dist/lib/consolidation.js +6 -5
- package/dist/lib/database.js +2 -2
- package/dist/lib/db-daemon-client.js +2 -2
- package/dist/lib/db.js +2 -2
- package/dist/lib/embed-worker.js +1 -0
- package/dist/lib/embedder.js +7 -3
- package/dist/lib/employee-templates.js +4 -4
- package/dist/lib/employees.js +2 -2
- package/dist/lib/exe-daemon-client.js +2 -2
- package/dist/lib/exe-daemon.js +160 -79
- package/dist/lib/hybrid-search.js +5 -5
- package/dist/lib/identity.js +2 -2
- package/dist/lib/messaging.js +11 -11
- package/dist/lib/reminders.js +3 -3
- package/dist/lib/schedules.js +5 -5
- package/dist/lib/session-registry.js +4 -4
- package/dist/lib/skill-learning.js +6 -6
- package/dist/lib/store.js +4 -4
- package/dist/lib/task-router.js +3 -3
- package/dist/lib/tasks.js +12 -12
- package/dist/lib/tmux-routing.js +12 -10
- package/dist/lib/tmux-transport.js +1 -1
- package/dist/lib/token-spend.js +3 -3
- package/dist/lib/transport.js +2 -2
- package/dist/mcp/register-tools.js +62 -61
- package/dist/mcp/server.js +63 -62
- package/dist/mcp/tools/complete-reminder.js +4 -4
- package/dist/mcp/tools/create-reminder.js +4 -4
- package/dist/mcp/tools/create-task.js +14 -14
- package/dist/mcp/tools/deactivate-behavior.js +7 -7
- package/dist/mcp/tools/list-reminders.js +4 -4
- package/dist/mcp/tools/list-tasks.js +14 -14
- package/dist/mcp/tools/send-message.js +13 -13
- package/dist/mcp/tools/update-task.js +13 -13
- package/dist/mcp-http-config-PQTOLCTP.js +29 -0
- package/dist/memory-cards-4RVDZIY2.js +180 -0
- package/dist/memory-graph-extractor-L6YC7G4M.js +22 -0
- package/dist/memory-poisoning-defense-4YVJYH4G.js +224 -0
- package/dist/memory-queue-client-MVAUOZNJ.js +16 -0
- package/dist/memory-reflection-SHHDQNOH.js +244 -0
- package/dist/message-queue-client-DCKZT6X2.js +92 -0
- package/dist/notifications-JFR3G42W.js +47 -0
- package/dist/orchestration-events-MGCGPTDN.js +27 -0
- package/dist/orchestrator-DAFL2YZB.js +35 -0
- package/dist/pipeline-router-WWSZVPCH.js +15 -0
- package/dist/plan-limits-C7XCSDZC.js +28 -0
- package/dist/project-boot-N3NTBVLE.js +299 -0
- package/dist/projection-worker-MTPAPCWX.js +1084 -0
- package/dist/prospective-memory-BTIVUJSB.js +232 -0
- package/dist/reranker-UA6WVESJ.js +19 -0
- package/dist/retrieval-health-7XNZJEBF.js +12 -0
- package/dist/review-polling-4ALGMXC3.js +126 -0
- package/dist/runtime/index.js +13 -13
- package/dist/self-query-router-MROFQLQB.js +192 -0
- package/dist/session-events-CK44XOU4.js +38 -0
- package/dist/session-kill-telemetry-MT6ITDOG.js +31 -0
- package/dist/session-scope-3XDBWV65.js +88 -0
- package/dist/setup-wizard-X6DOD7MC.js +12 -0
- package/dist/skill-refinement-G2CCY3GM.js +159 -0
- package/dist/stack-update-JF7F56AS.js +84 -0
- package/dist/steward-gate-YF2CYXE7.js +15 -0
- package/dist/task-enforcement-YN6HK7NE.js +506 -0
- package/dist/task-scope-CVK6ISCZ.js +37 -0
- package/dist/tasks-crud-NTNET4JE.js +79 -0
- package/dist/tasks-notify-4LJVFPCV.js +40 -0
- package/dist/tasks-review-3V4WOIRG.js +49 -0
- package/dist/telemetry-upload-5PNUKGTM.js +741 -0
- package/dist/token-budget-E46G7ZAQ.js +86 -0
- package/dist/tool-capability-index-JDSMKJER.js +10 -0
- package/dist/tool-telemetry-J3NLS3LJ.js +17 -0
- package/dist/tui/App.js +18 -18
- package/dist/tui-data-6DOMUUCM.js +260 -0
- package/dist/wiki-acl-5UK37LKF.js +111 -0
- package/dist/worker-gate-FM7AEC7G.js +21 -0
- package/dist/workflow-engine-2EDUHUIY.js +28 -0
- package/dist/worktree-7YKKJIYR.js +28 -0
- package/dist/worktree-sweep-C3ELFGDN.js +21 -0
- package/package.json +1 -1
- package/release-notes.json +88 -88
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
import {
|
|
2
|
+
supportAdminHeaders
|
|
3
|
+
} from "./chunk-2XZ6X3PJ.js";
|
|
4
|
+
import {
|
|
5
|
+
getOrchestrationMetrics
|
|
6
|
+
} from "./chunk-NLZHVIOP.js";
|
|
7
|
+
import "./chunk-WUKHLCBE.js";
|
|
8
|
+
import "./chunk-2I23RPSI.js";
|
|
9
|
+
import "./chunk-2E43UXRH.js";
|
|
10
|
+
import "./chunk-PNQDP3OA.js";
|
|
11
|
+
import "./chunk-7HLWBYH7.js";
|
|
12
|
+
import "./chunk-FXU7JOXK.js";
|
|
13
|
+
import {
|
|
14
|
+
loadLicense,
|
|
15
|
+
readCachedLicenseToken
|
|
16
|
+
} from "./chunk-YTKVJJSU.js";
|
|
17
|
+
import {
|
|
18
|
+
loadConfig
|
|
19
|
+
} from "./chunk-R36FAN53.js";
|
|
20
|
+
import "./chunk-LYH5HE24.js";
|
|
21
|
+
import "./chunk-MLKGABMK.js";
|
|
22
|
+
|
|
23
|
+
// src/lib/telemetry-upload.ts
|
|
24
|
+
import { readFileSync } from "fs";
|
|
25
|
+
import os from "os";
|
|
26
|
+
import { join as joinPath } from "path";
|
|
27
|
+
|
|
28
|
+
// src/lib/telemetry-insights.ts
|
|
29
|
+
var SEVERITY_ORDER = {
|
|
30
|
+
bug: 0,
|
|
31
|
+
inefficiency: 1,
|
|
32
|
+
improvement: 2
|
|
33
|
+
};
|
|
34
|
+
function computeInsights(payload) {
|
|
35
|
+
const insights = [];
|
|
36
|
+
const counts = payload.metrics?.counts;
|
|
37
|
+
if (!counts && !payload.usage && !payload.calibration && !payload.toolUsage) {
|
|
38
|
+
return insights;
|
|
39
|
+
}
|
|
40
|
+
if (counts) {
|
|
41
|
+
if (counts.eventLoopBlocks > 0) {
|
|
42
|
+
insights.push({
|
|
43
|
+
severity: "bug",
|
|
44
|
+
category: "daemon",
|
|
45
|
+
title: `Daemon event loop blocked ${counts.eventLoopBlocks} times`,
|
|
46
|
+
detail: "MCP unresponsive periods \u2014 agents may have lost connection during these blocks",
|
|
47
|
+
metric: "eventLoopBlocks",
|
|
48
|
+
value: counts.eventLoopBlocks,
|
|
49
|
+
threshold: 0
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (counts.mcpUnreachable > 0) {
|
|
53
|
+
insights.push({
|
|
54
|
+
severity: "bug",
|
|
55
|
+
category: "daemon",
|
|
56
|
+
title: `MCP server unreachable ${counts.mcpUnreachable} times`,
|
|
57
|
+
detail: "Agents lost connection to the MCP server \u2014 tasks may have stalled",
|
|
58
|
+
metric: "mcpUnreachable",
|
|
59
|
+
value: counts.mcpUnreachable,
|
|
60
|
+
threshold: 0
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (counts.shardCircuitOpens > 0) {
|
|
64
|
+
insights.push({
|
|
65
|
+
severity: "bug",
|
|
66
|
+
category: "memory",
|
|
67
|
+
title: `Shard circuit breaker opened ${counts.shardCircuitOpens} times`,
|
|
68
|
+
detail: "DB encryption or shard routing issues \u2014 memory operations may have failed",
|
|
69
|
+
metric: "shardCircuitOpens",
|
|
70
|
+
value: counts.shardCircuitOpens,
|
|
71
|
+
threshold: 0
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
if (counts.consistencyMismatches > 0) {
|
|
75
|
+
insights.push({
|
|
76
|
+
severity: "bug",
|
|
77
|
+
category: "orchestration",
|
|
78
|
+
title: `Data consistency mismatches detected (${counts.consistencyMismatches})`,
|
|
79
|
+
detail: "Task DB vs file state diverged \u2014 possible concurrent write corruption",
|
|
80
|
+
metric: "consistencyMismatches",
|
|
81
|
+
value: counts.consistencyMismatches,
|
|
82
|
+
threshold: 0
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (counts.dispatchFailed > 0) {
|
|
86
|
+
insights.push({
|
|
87
|
+
severity: "bug",
|
|
88
|
+
category: "orchestration",
|
|
89
|
+
title: `Task dispatch failed ${counts.dispatchFailed} times`,
|
|
90
|
+
detail: "Agents not receiving work \u2014 check tmux session health and dispatch routing",
|
|
91
|
+
metric: "dispatchFailed",
|
|
92
|
+
value: counts.dispatchFailed,
|
|
93
|
+
threshold: 0
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if (counts.tmuxCommandTimeouts > 0) {
|
|
97
|
+
insights.push({
|
|
98
|
+
severity: "bug",
|
|
99
|
+
category: "daemon",
|
|
100
|
+
title: `tmux commands timed out ${counts.tmuxCommandTimeouts} times`,
|
|
101
|
+
detail: "Session management delays \u2014 agent spawning and communication affected",
|
|
102
|
+
metric: "tmuxCommandTimeouts",
|
|
103
|
+
value: counts.tmuxCommandTimeouts,
|
|
104
|
+
threshold: 0
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const reviewToApproved = payload.metrics?.reviewToApprovedMs;
|
|
109
|
+
if (reviewToApproved && reviewToApproved.p95 !== null && reviewToApproved.p95 > 18e5) {
|
|
110
|
+
const mins = Math.round(reviewToApproved.p95 / 6e4);
|
|
111
|
+
insights.push({
|
|
112
|
+
severity: "inefficiency",
|
|
113
|
+
category: "orchestration",
|
|
114
|
+
title: `Review bottleneck: p95 review time is ${mins}min`,
|
|
115
|
+
detail: "Work sitting idle waiting for review \u2014 consider auto-approve for low-risk tasks",
|
|
116
|
+
metric: "reviewToApprovedMs.p95",
|
|
117
|
+
value: reviewToApproved.p95,
|
|
118
|
+
threshold: 18e5
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const createToClaim = payload.metrics?.createToClaimMs;
|
|
122
|
+
if (createToClaim && createToClaim.p95 !== null && createToClaim.p95 > 3e5) {
|
|
123
|
+
const mins = Math.round(createToClaim.p95 / 6e4);
|
|
124
|
+
insights.push({
|
|
125
|
+
severity: "inefficiency",
|
|
126
|
+
category: "orchestration",
|
|
127
|
+
title: `Slow task pickup: p95 claim time is ${mins}min`,
|
|
128
|
+
detail: "Agents not auto-chaining \u2014 tasks are waiting to be claimed",
|
|
129
|
+
metric: "createToClaimMs.p95",
|
|
130
|
+
value: createToClaim.p95,
|
|
131
|
+
threshold: 3e5
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
if (counts && counts.sessionBorns > 0) {
|
|
135
|
+
const idleKillRatio = counts.sessionIdleKills / counts.sessionBorns;
|
|
136
|
+
if (idleKillRatio > 0.3) {
|
|
137
|
+
const pct = Math.round(idleKillRatio * 100);
|
|
138
|
+
insights.push({
|
|
139
|
+
severity: "inefficiency",
|
|
140
|
+
category: "orchestration",
|
|
141
|
+
title: `${pct}% of sessions idle-killed`,
|
|
142
|
+
detail: "Spawning agents that don't work \u2014 reduce unnecessary session creation",
|
|
143
|
+
metric: "sessionIdleKills/sessionBorns",
|
|
144
|
+
value: idleKillRatio,
|
|
145
|
+
threshold: 0.3
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (payload.calibration?.estimation) {
|
|
150
|
+
const est = payload.calibration.estimation;
|
|
151
|
+
if (est.total > 0 && est.avgRatio > 2) {
|
|
152
|
+
insights.push({
|
|
153
|
+
severity: "inefficiency",
|
|
154
|
+
category: "calibration",
|
|
155
|
+
title: `Chronic underestimation: agents estimate ${est.avgRatio.toFixed(1)}x less than actual`,
|
|
156
|
+
detail: "Tasks consistently take longer than estimated \u2014 affects scheduling accuracy",
|
|
157
|
+
metric: "estimation.avgRatio",
|
|
158
|
+
value: est.avgRatio,
|
|
159
|
+
threshold: 2
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
if (est.total > 0 && est.avgRatio < 0.5) {
|
|
163
|
+
insights.push({
|
|
164
|
+
severity: "inefficiency",
|
|
165
|
+
category: "calibration",
|
|
166
|
+
title: `Chronic overestimation: agents estimate ${est.avgRatio.toFixed(1)}x more than actual`,
|
|
167
|
+
detail: "Tasks consistently finish faster than estimated \u2014 capacity being underutilized",
|
|
168
|
+
metric: "estimation.avgRatio",
|
|
169
|
+
value: est.avgRatio,
|
|
170
|
+
threshold: 0.5
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (payload.calibration?.assertions) {
|
|
175
|
+
const a = payload.calibration.assertions;
|
|
176
|
+
if (a.total > 0) {
|
|
177
|
+
const accuracy = a.correct / a.total;
|
|
178
|
+
if (accuracy < 0.6) {
|
|
179
|
+
const pct = Math.round(accuracy * 100);
|
|
180
|
+
insights.push({
|
|
181
|
+
severity: "inefficiency",
|
|
182
|
+
category: "calibration",
|
|
183
|
+
title: `Low assertion accuracy (${pct}%)`,
|
|
184
|
+
detail: "Agents making wrong calls more than 40% of the time \u2014 review calibration data",
|
|
185
|
+
metric: "assertions.accuracy",
|
|
186
|
+
value: accuracy,
|
|
187
|
+
threshold: 0.6
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (payload.calibration?.whyWrong) {
|
|
193
|
+
const entries = Object.entries(payload.calibration.whyWrong);
|
|
194
|
+
if (entries.length > 0) {
|
|
195
|
+
entries.sort(([, a], [, b]) => b - a);
|
|
196
|
+
const [topCategory, topCount] = entries[0];
|
|
197
|
+
insights.push({
|
|
198
|
+
severity: "inefficiency",
|
|
199
|
+
category: "calibration",
|
|
200
|
+
title: `Most common failure: ${topCategory} (${topCount} times)`,
|
|
201
|
+
detail: "Recurring failure pattern \u2014 consider adding targeted behaviors or guardrails",
|
|
202
|
+
metric: "whyWrong.topCategory",
|
|
203
|
+
value: topCount,
|
|
204
|
+
threshold: 0
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (payload.usage) {
|
|
209
|
+
if (payload.usage.dreamCyclesRun24h === 0) {
|
|
210
|
+
insights.push({
|
|
211
|
+
severity: "improvement",
|
|
212
|
+
category: "adoption",
|
|
213
|
+
title: "No dream cycles in 24h",
|
|
214
|
+
detail: "Session-end hook may not be firing \u2014 dream cycles consolidate session learnings",
|
|
215
|
+
metric: "dreamCyclesRun24h",
|
|
216
|
+
value: 0,
|
|
217
|
+
threshold: 1
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (payload.usage.assertionsLogged24h === 0 && payload.usage.tasksCompleted24h > 0) {
|
|
221
|
+
insights.push({
|
|
222
|
+
severity: "improvement",
|
|
223
|
+
category: "adoption",
|
|
224
|
+
title: "Tasks completed without assertions",
|
|
225
|
+
detail: "Confidence tracking not adopted \u2014 assertions improve calibration accuracy",
|
|
226
|
+
metric: "assertionsLogged24h",
|
|
227
|
+
value: 0,
|
|
228
|
+
threshold: 1
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (payload.usage.agentCount > 0) {
|
|
232
|
+
const avgBehaviors = payload.usage.behaviorCount / payload.usage.agentCount;
|
|
233
|
+
if (avgBehaviors > 50) {
|
|
234
|
+
insights.push({
|
|
235
|
+
severity: "improvement",
|
|
236
|
+
category: "memory",
|
|
237
|
+
title: `Behavior bloat (${Math.round(avgBehaviors)} avg behaviors per agent)`,
|
|
238
|
+
detail: "Too many behaviors per agent \u2014 needs consolidation to reduce context size",
|
|
239
|
+
metric: "behaviorCount/agentCount",
|
|
240
|
+
value: avgBehaviors,
|
|
241
|
+
threshold: 50
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (payload.toolUsage) {
|
|
247
|
+
for (const [name, usage] of Object.entries(payload.toolUsage)) {
|
|
248
|
+
if (usage.count > 0) {
|
|
249
|
+
const errorRate = usage.errors / usage.count;
|
|
250
|
+
if (errorRate > 0.1) {
|
|
251
|
+
const pct = Math.round(errorRate * 100);
|
|
252
|
+
insights.push({
|
|
253
|
+
severity: "improvement",
|
|
254
|
+
category: "adoption",
|
|
255
|
+
title: `Tool ${name} has ${pct}% error rate`,
|
|
256
|
+
detail: "High tool failure rate \u2014 investigate tool configuration or input patterns",
|
|
257
|
+
metric: `toolUsage.${name}.errorRate`,
|
|
258
|
+
value: errorRate,
|
|
259
|
+
threshold: 0.1
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (counts) {
|
|
266
|
+
if (counts.contextPressure > 5) {
|
|
267
|
+
insights.push({
|
|
268
|
+
severity: "improvement",
|
|
269
|
+
category: "adoption",
|
|
270
|
+
title: `High context pressure (${counts.contextPressure} events)`,
|
|
271
|
+
detail: "Agents hitting context limits frequently \u2014 consider shorter sessions or better memory pruning",
|
|
272
|
+
metric: "contextPressure",
|
|
273
|
+
value: counts.contextPressure,
|
|
274
|
+
threshold: 5
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (counts.missedReviews > 0) {
|
|
278
|
+
insights.push({
|
|
279
|
+
severity: "improvement",
|
|
280
|
+
category: "orchestration",
|
|
281
|
+
title: `${counts.missedReviews} missed reviews`,
|
|
282
|
+
detail: "Review notifications not reaching reviewers \u2014 check review polling configuration",
|
|
283
|
+
metric: "missedReviews",
|
|
284
|
+
value: counts.missedReviews,
|
|
285
|
+
threshold: 0
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (payload.investigations) {
|
|
290
|
+
const inv = payload.investigations;
|
|
291
|
+
if (inv.avgAttemptsPerBug > 3) {
|
|
292
|
+
insights.push({
|
|
293
|
+
severity: "inefficiency",
|
|
294
|
+
category: "investigation",
|
|
295
|
+
title: `Agents averaging ${inv.avgAttemptsPerBug.toFixed(1)} attempts per bug`,
|
|
296
|
+
detail: "High retry count \u2014 investigate reasoning quality and context availability",
|
|
297
|
+
metric: "investigations.avgAttemptsPerBug",
|
|
298
|
+
value: inv.avgAttemptsPerBug,
|
|
299
|
+
threshold: 3
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (inv.firstAttemptSuccessRate < 0.3 && inv.totalInvestigations >= 3) {
|
|
303
|
+
const pct = Math.round(inv.firstAttemptSuccessRate * 100);
|
|
304
|
+
insights.push({
|
|
305
|
+
severity: "inefficiency",
|
|
306
|
+
category: "investigation",
|
|
307
|
+
title: `Only ${pct}% of bugs fixed on first attempt`,
|
|
308
|
+
detail: "Low first-attempt success \u2014 agents may need better diagnostics before editing",
|
|
309
|
+
metric: "investigations.firstAttemptSuccessRate",
|
|
310
|
+
value: inv.firstAttemptSuccessRate,
|
|
311
|
+
threshold: 0.3
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (inv.failureCategoryDistribution) {
|
|
315
|
+
const entries = Object.entries(inv.failureCategoryDistribution);
|
|
316
|
+
if (entries.length > 0) {
|
|
317
|
+
entries.sort(([, a], [, b]) => b - a);
|
|
318
|
+
const [topCategory, topCount] = entries[0];
|
|
319
|
+
if (topCategory === "lack_of_tools" && topCount >= 2) {
|
|
320
|
+
insights.push({
|
|
321
|
+
severity: "improvement",
|
|
322
|
+
category: "investigation",
|
|
323
|
+
title: `"lack_of_tools" is dominant failure category (${topCount} times)`,
|
|
324
|
+
detail: "Agents lack verification tools \u2014 ensure playwright/curl available",
|
|
325
|
+
metric: "investigations.failureCategory.lack_of_tools",
|
|
326
|
+
value: topCount,
|
|
327
|
+
threshold: 2
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
if (topCategory === "lack_of_context" && topCount >= 2) {
|
|
331
|
+
insights.push({
|
|
332
|
+
severity: "improvement",
|
|
333
|
+
category: "investigation",
|
|
334
|
+
title: `"lack_of_context" is dominant failure category (${topCount} times)`,
|
|
335
|
+
detail: "Agents missing context \u2014 check if relevant code is indexed",
|
|
336
|
+
metric: "investigations.failureCategory.lack_of_context",
|
|
337
|
+
value: topCount,
|
|
338
|
+
threshold: 2
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
if (inv.avgWastedMinutes > 5) {
|
|
344
|
+
insights.push({
|
|
345
|
+
severity: "inefficiency",
|
|
346
|
+
category: "investigation",
|
|
347
|
+
title: `Average ${inv.avgWastedMinutes.toFixed(1)} minutes wasted per bug on failed attempts`,
|
|
348
|
+
detail: "Significant time lost on wrong paths \u2014 improve initial diagnosis accuracy",
|
|
349
|
+
metric: "investigations.avgWastedMinutes",
|
|
350
|
+
value: inv.avgWastedMinutes,
|
|
351
|
+
threshold: 5
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
insights.sort((a, b) => SEVERITY_ORDER[a.severity] - SEVERITY_ORDER[b.severity]);
|
|
356
|
+
return insights;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
// src/lib/telemetry-upload.ts
|
|
360
|
+
function isTelemetryEnabled() {
|
|
361
|
+
if (process.env.EXE_TELEMETRY === "0") {
|
|
362
|
+
try {
|
|
363
|
+
const configPath = joinPath(os.homedir(), ".exe-os", "config.json");
|
|
364
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
365
|
+
const plan = String(cfg?.license?.plan ?? cfg?.cloud?.plan ?? "free").toLowerCase();
|
|
366
|
+
if (plan === "free" || plan === "" || plan === "unknown") {
|
|
367
|
+
return true;
|
|
368
|
+
}
|
|
369
|
+
} catch {
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
async function buildTelemetryPayload(sinceHours = 24) {
|
|
376
|
+
const sinceIso = new Date(Date.now() - sinceHours * 60 * 60 * 1e3).toISOString();
|
|
377
|
+
const metrics = sanitizeMetricsForUpload(await getOrchestrationMetrics({ sinceIso }));
|
|
378
|
+
let version = "unknown";
|
|
379
|
+
try {
|
|
380
|
+
const { readFileSync: readFileSync2 } = await import("fs");
|
|
381
|
+
const { join, dirname } = await import("path");
|
|
382
|
+
const { fileURLToPath } = await import("url");
|
|
383
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
384
|
+
for (let i = 0; i < 5; i++) {
|
|
385
|
+
try {
|
|
386
|
+
const pkg = JSON.parse(readFileSync2(join(dir, "package.json"), "utf-8"));
|
|
387
|
+
if (pkg.version) {
|
|
388
|
+
version = pkg.version;
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
} catch {
|
|
392
|
+
}
|
|
393
|
+
dir = dirname(dir);
|
|
394
|
+
}
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
let toolUsage;
|
|
398
|
+
try {
|
|
399
|
+
const { getToolUsageSummary } = await import("./tool-telemetry-J3NLS3LJ.js");
|
|
400
|
+
const summary = getToolUsageSummary();
|
|
401
|
+
if (Object.keys(summary).length > 0) toolUsage = summary;
|
|
402
|
+
} catch {
|
|
403
|
+
}
|
|
404
|
+
let calibration;
|
|
405
|
+
try {
|
|
406
|
+
const { DreamingEngine } = await import("./dreaming-SK5VEQRF.js");
|
|
407
|
+
const engine = new DreamingEngine();
|
|
408
|
+
const cal = await engine.computeCalibration("*", sinceHours);
|
|
409
|
+
if (cal.assertions.total > 0 || cal.estimation.total > 0) {
|
|
410
|
+
calibration = cal;
|
|
411
|
+
}
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
let usage;
|
|
415
|
+
try {
|
|
416
|
+
const { getClient } = await import("./lib/database.js");
|
|
417
|
+
const db = getClient();
|
|
418
|
+
const since = sinceIso;
|
|
419
|
+
let memoryCount = 0;
|
|
420
|
+
try {
|
|
421
|
+
const r = await db.execute("SELECT COUNT(*) AS cnt FROM memories");
|
|
422
|
+
memoryCount = Number(r.rows[0]?.cnt ?? 0);
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
let agentCount = 0;
|
|
426
|
+
try {
|
|
427
|
+
const r = await db.execute("SELECT COUNT(DISTINCT agent_id) AS cnt FROM memories");
|
|
428
|
+
agentCount = Number(r.rows[0]?.cnt ?? 0);
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
let sessionCount24h = 0;
|
|
432
|
+
try {
|
|
433
|
+
const r = await db.execute({
|
|
434
|
+
sql: "SELECT COUNT(*) AS cnt FROM orchestration_events WHERE event_type = 'session.born' AND ts >= ?",
|
|
435
|
+
args: [since]
|
|
436
|
+
});
|
|
437
|
+
sessionCount24h = Number(r.rows[0]?.cnt ?? 0);
|
|
438
|
+
} catch {
|
|
439
|
+
}
|
|
440
|
+
let tasksCreated24h = 0;
|
|
441
|
+
try {
|
|
442
|
+
const r = await db.execute({ sql: "SELECT COUNT(*) AS cnt FROM tasks WHERE created_at >= ?", args: [since] });
|
|
443
|
+
tasksCreated24h = Number(r.rows[0]?.cnt ?? 0);
|
|
444
|
+
} catch {
|
|
445
|
+
}
|
|
446
|
+
let tasksCompleted24h = 0;
|
|
447
|
+
try {
|
|
448
|
+
const r = await db.execute({
|
|
449
|
+
sql: "SELECT COUNT(*) AS cnt FROM tasks WHERE status IN ('needs_review', 'closed') AND updated_at >= ?",
|
|
450
|
+
args: [since]
|
|
451
|
+
});
|
|
452
|
+
tasksCompleted24h = Number(r.rows[0]?.cnt ?? 0);
|
|
453
|
+
} catch {
|
|
454
|
+
}
|
|
455
|
+
let tasksCancelled24h = 0;
|
|
456
|
+
try {
|
|
457
|
+
const r = await db.execute({
|
|
458
|
+
sql: "SELECT COUNT(*) AS cnt FROM tasks WHERE status = 'cancelled' AND updated_at >= ?",
|
|
459
|
+
args: [since]
|
|
460
|
+
});
|
|
461
|
+
tasksCancelled24h = Number(r.rows[0]?.cnt ?? 0);
|
|
462
|
+
} catch {
|
|
463
|
+
}
|
|
464
|
+
let behaviorCount = 0;
|
|
465
|
+
try {
|
|
466
|
+
const r = await db.execute("SELECT COUNT(*) AS cnt FROM behaviors WHERE active = 1");
|
|
467
|
+
behaviorCount = Number(r.rows[0]?.cnt ?? 0);
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
let dreamCyclesRun24h = 0;
|
|
471
|
+
try {
|
|
472
|
+
const r = await db.execute({
|
|
473
|
+
sql: "SELECT COUNT(*) AS cnt FROM memories WHERE memory_type = 'observation' AND raw_text LIKE '%DREAM CYCLE%' AND timestamp >= ?",
|
|
474
|
+
args: [since]
|
|
475
|
+
});
|
|
476
|
+
dreamCyclesRun24h = Number(r.rows[0]?.cnt ?? 0);
|
|
477
|
+
} catch {
|
|
478
|
+
}
|
|
479
|
+
let assertionsLogged24h = 0;
|
|
480
|
+
try {
|
|
481
|
+
const r = await db.execute({
|
|
482
|
+
sql: "SELECT COUNT(*) AS cnt FROM tasks WHERE assertions IS NOT NULL AND updated_at >= ?",
|
|
483
|
+
args: [since]
|
|
484
|
+
});
|
|
485
|
+
assertionsLogged24h = Number(r.rows[0]?.cnt ?? 0);
|
|
486
|
+
} catch {
|
|
487
|
+
}
|
|
488
|
+
let cloudSyncHealthy = false;
|
|
489
|
+
try {
|
|
490
|
+
const r = await db.execute(
|
|
491
|
+
"SELECT value FROM sync_meta WHERE key = 'last_cloud_push_version'"
|
|
492
|
+
);
|
|
493
|
+
cloudSyncHealthy = r.rows.length > 0 && !!r.rows[0]?.value;
|
|
494
|
+
} catch {
|
|
495
|
+
}
|
|
496
|
+
usage = {
|
|
497
|
+
memoryCount,
|
|
498
|
+
agentCount,
|
|
499
|
+
sessionCount24h,
|
|
500
|
+
tasksCreated24h,
|
|
501
|
+
tasksCompleted24h,
|
|
502
|
+
tasksCancelled24h,
|
|
503
|
+
behaviorCount,
|
|
504
|
+
dreamCyclesRun24h,
|
|
505
|
+
assertionsLogged24h,
|
|
506
|
+
cloudSyncHealthy
|
|
507
|
+
};
|
|
508
|
+
} catch {
|
|
509
|
+
}
|
|
510
|
+
let investigations;
|
|
511
|
+
try {
|
|
512
|
+
const { getClient: getInvClient } = await import("./lib/database.js");
|
|
513
|
+
const invDb = getInvClient();
|
|
514
|
+
const invCountResult = await invDb.execute({
|
|
515
|
+
sql: `SELECT COUNT(DISTINCT investigation_key) AS cnt FROM investigation_attempts WHERE timestamp >= ?`,
|
|
516
|
+
args: [sinceIso]
|
|
517
|
+
});
|
|
518
|
+
const totalInvestigations = Number(invCountResult.rows[0]?.cnt ?? 0);
|
|
519
|
+
if (totalInvestigations > 0) {
|
|
520
|
+
const avgResult = await invDb.execute({
|
|
521
|
+
sql: `SELECT AVG(max_attempt) AS avg_attempts FROM (
|
|
522
|
+
SELECT investigation_key, MAX(attempt_number) AS max_attempt
|
|
523
|
+
FROM investigation_attempts WHERE timestamp >= ?
|
|
524
|
+
GROUP BY investigation_key
|
|
525
|
+
)`,
|
|
526
|
+
args: [sinceIso]
|
|
527
|
+
});
|
|
528
|
+
const avgAttemptsPerBug = Number(avgResult.rows[0]?.avg_attempts ?? 0);
|
|
529
|
+
const wastedResult = await invDb.execute({
|
|
530
|
+
sql: `SELECT COALESCE(SUM(duration_ms), 0) AS wasted FROM investigation_attempts
|
|
531
|
+
WHERE timestamp >= ? AND result != 'pass'`,
|
|
532
|
+
args: [sinceIso]
|
|
533
|
+
});
|
|
534
|
+
const avgWastedMinutes = Number(wastedResult.rows[0]?.wasted ?? 0) / 6e4 / totalInvestigations;
|
|
535
|
+
const catResult = await invDb.execute({
|
|
536
|
+
sql: `SELECT failure_category, COUNT(*) AS cnt FROM investigation_attempts
|
|
537
|
+
WHERE timestamp >= ? AND failure_category IS NOT NULL
|
|
538
|
+
GROUP BY failure_category`,
|
|
539
|
+
args: [sinceIso]
|
|
540
|
+
});
|
|
541
|
+
const failureCategoryDistribution = {};
|
|
542
|
+
for (const row of catResult.rows) {
|
|
543
|
+
failureCategoryDistribution[String(row.failure_category)] = Number(row.cnt);
|
|
544
|
+
}
|
|
545
|
+
const firstAttemptResult = await invDb.execute({
|
|
546
|
+
sql: `SELECT
|
|
547
|
+
COUNT(DISTINCT CASE WHEN attempt_number = 1 AND result = 'pass' THEN investigation_key END) AS first_pass,
|
|
548
|
+
COUNT(DISTINCT investigation_key) AS total
|
|
549
|
+
FROM investigation_attempts WHERE timestamp >= ?`,
|
|
550
|
+
args: [sinceIso]
|
|
551
|
+
});
|
|
552
|
+
const firstPass = Number(firstAttemptResult.rows[0]?.first_pass ?? 0);
|
|
553
|
+
const totalForRate = Number(firstAttemptResult.rows[0]?.total ?? 1);
|
|
554
|
+
const firstAttemptSuccessRate = totalForRate > 0 ? firstPass / totalForRate : 0;
|
|
555
|
+
investigations = {
|
|
556
|
+
totalInvestigations,
|
|
557
|
+
avgAttemptsPerBug,
|
|
558
|
+
avgWastedMinutes,
|
|
559
|
+
failureCategoryDistribution,
|
|
560
|
+
firstAttemptSuccessRate
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
} catch {
|
|
564
|
+
}
|
|
565
|
+
const payload = {
|
|
566
|
+
version,
|
|
567
|
+
nodeVersion: process.version,
|
|
568
|
+
platform: process.platform,
|
|
569
|
+
arch: process.arch,
|
|
570
|
+
totalMemGB: Math.round(os.totalmem() / 1024 ** 3),
|
|
571
|
+
cpuCount: os.cpus().length,
|
|
572
|
+
uptimeHours: Math.round(os.uptime() / 3600),
|
|
573
|
+
metrics,
|
|
574
|
+
toolUsage,
|
|
575
|
+
calibration,
|
|
576
|
+
usage,
|
|
577
|
+
investigations
|
|
578
|
+
};
|
|
579
|
+
try {
|
|
580
|
+
const insights = computeInsights(payload);
|
|
581
|
+
if (insights.length > 0) payload.insights = insights;
|
|
582
|
+
} catch {
|
|
583
|
+
}
|
|
584
|
+
return payload;
|
|
585
|
+
}
|
|
586
|
+
function sanitizeMetricsForUpload(metrics) {
|
|
587
|
+
const agentAliases = /* @__PURE__ */ new Map();
|
|
588
|
+
const reviewerAliases = /* @__PURE__ */ new Map();
|
|
589
|
+
const alias = (prefix, value, map) => {
|
|
590
|
+
if (!map.has(value)) map.set(value, `${prefix}_${map.size + 1}`);
|
|
591
|
+
return map.get(value);
|
|
592
|
+
};
|
|
593
|
+
return {
|
|
594
|
+
...metrics,
|
|
595
|
+
resumeStorms: metrics.resumeStorms.map((storm) => ({
|
|
596
|
+
...storm,
|
|
597
|
+
agentId: alias("agent", storm.agentId, agentAliases)
|
|
598
|
+
})),
|
|
599
|
+
reviewerBottlenecks: metrics.reviewerBottlenecks.map((bottleneck) => ({
|
|
600
|
+
...bottleneck,
|
|
601
|
+
reviewer: alias("reviewer", bottleneck.reviewer, reviewerAliases)
|
|
602
|
+
})),
|
|
603
|
+
reviewerBacklogs: metrics.reviewerBacklogs.map((backlog) => ({
|
|
604
|
+
...backlog,
|
|
605
|
+
reviewer: alias("reviewer", backlog.reviewer, reviewerAliases)
|
|
606
|
+
}))
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
var OUTBOX_DIR_NAME = "telemetry-outbox";
|
|
610
|
+
var MAX_OUTBOX_FILES = 30;
|
|
611
|
+
async function getOutboxDir() {
|
|
612
|
+
const { join } = await import("path");
|
|
613
|
+
const { homedir } = await import("os");
|
|
614
|
+
const dir = join(homedir(), ".exe-os", OUTBOX_DIR_NAME);
|
|
615
|
+
const { mkdirSync } = await import("fs");
|
|
616
|
+
mkdirSync(dir, { recursive: true });
|
|
617
|
+
return dir;
|
|
618
|
+
}
|
|
619
|
+
async function persistToOutbox(payload) {
|
|
620
|
+
try {
|
|
621
|
+
const { join } = await import("path");
|
|
622
|
+
const { writeFileSync, readdirSync, unlinkSync } = await import("fs");
|
|
623
|
+
const dir = await getOutboxDir();
|
|
624
|
+
const file = join(dir, `${Date.now()}.json`);
|
|
625
|
+
writeFileSync(file, JSON.stringify(payload), "utf-8");
|
|
626
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json")).sort();
|
|
627
|
+
while (files.length > MAX_OUTBOX_FILES) {
|
|
628
|
+
try {
|
|
629
|
+
unlinkSync(join(dir, files.shift()));
|
|
630
|
+
} catch {
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
} catch {
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
async function drainOutbox(sendFn) {
|
|
637
|
+
let sent = 0;
|
|
638
|
+
try {
|
|
639
|
+
const { join } = await import("path");
|
|
640
|
+
const { readFileSync: readFileSync2, readdirSync, unlinkSync } = await import("fs");
|
|
641
|
+
const dir = await getOutboxDir();
|
|
642
|
+
const files = readdirSync(dir).filter((f) => f.endsWith(".json")).sort();
|
|
643
|
+
for (const file of files.slice(0, 2)) {
|
|
644
|
+
try {
|
|
645
|
+
const payload = JSON.parse(readFileSync2(join(dir, file), "utf-8"));
|
|
646
|
+
const result = await sendFn(payload);
|
|
647
|
+
if (result === "sent") {
|
|
648
|
+
unlinkSync(join(dir, file));
|
|
649
|
+
sent++;
|
|
650
|
+
if (files.length > 1) await new Promise((r) => setTimeout(r, 1e4));
|
|
651
|
+
} else if (result.includes("429") || result.includes("rate")) {
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
} catch {
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
} catch {
|
|
658
|
+
}
|
|
659
|
+
return sent;
|
|
660
|
+
}
|
|
661
|
+
var KILLSWITCH_CACHE_FILE = "telemetry-killswitch.json";
|
|
662
|
+
var KILLSWITCH_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
663
|
+
async function isRemoteKillswitchActive() {
|
|
664
|
+
try {
|
|
665
|
+
const { join } = await import("path");
|
|
666
|
+
const { homedir } = await import("os");
|
|
667
|
+
const { readFileSync: readFileSync2, writeFileSync, existsSync } = await import("fs");
|
|
668
|
+
const cachePath = join(homedir(), ".exe-os", KILLSWITCH_CACHE_FILE);
|
|
669
|
+
if (existsSync(cachePath)) {
|
|
670
|
+
try {
|
|
671
|
+
const cached = JSON.parse(readFileSync2(cachePath, "utf-8"));
|
|
672
|
+
if (Date.now() - cached.checkedAt < KILLSWITCH_CACHE_TTL_MS) {
|
|
673
|
+
return cached.disabled;
|
|
674
|
+
}
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
const response = await fetch("https://api.askexe.com/v1/support/telemetry/status", {
|
|
679
|
+
method: "GET",
|
|
680
|
+
signal: AbortSignal.timeout(5e3)
|
|
681
|
+
});
|
|
682
|
+
const disabled = response.ok ? (await response.json()).disabled === true : false;
|
|
683
|
+
try {
|
|
684
|
+
writeFileSync(cachePath, JSON.stringify({ disabled, checkedAt: Date.now() }), "utf-8");
|
|
685
|
+
} catch {
|
|
686
|
+
}
|
|
687
|
+
return disabled;
|
|
688
|
+
} catch {
|
|
689
|
+
return false;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
async function sendTelemetryUpstream(payload) {
|
|
693
|
+
if (!isTelemetryEnabled()) return "disabled (EXE_TELEMETRY=0)";
|
|
694
|
+
if (await isRemoteKillswitchActive()) return "disabled (remote killswitch)";
|
|
695
|
+
const config = await loadConfig();
|
|
696
|
+
const routerUrl = process.env.API_ROUTER_URL?.replace(/\/+$/, "");
|
|
697
|
+
const endpoint = config.support?.bugReportEndpoint?.replace(/\/bug-reports\/?$/, "/telemetry") || (routerUrl ? `${routerUrl}/v1/support/telemetry` : "https://api.askexe.com/v1/support/telemetry");
|
|
698
|
+
const token = config.support?.bugReportToken || process.env.EXE_BUG_REPORT_TOKEN;
|
|
699
|
+
const licenseKey = loadLicense() || process.env.EXE_LICENSE_KEY || config.cloud?.apiKey;
|
|
700
|
+
const licenseToken = readCachedLicenseToken();
|
|
701
|
+
const doSend = async (p) => {
|
|
702
|
+
const parsed = new URL(endpoint);
|
|
703
|
+
if (parsed.protocol !== "https:" && !["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)) {
|
|
704
|
+
return "failed: insecure endpoint rejected";
|
|
705
|
+
}
|
|
706
|
+
const response = await fetch(parsed, {
|
|
707
|
+
method: "POST",
|
|
708
|
+
headers: {
|
|
709
|
+
"content-type": "application/json",
|
|
710
|
+
...token ? { authorization: `Bearer ${token}` } : {},
|
|
711
|
+
...licenseKey ? { "x-exe-license-key": licenseKey } : {},
|
|
712
|
+
...licenseToken ? { "x-exe-license-token": licenseToken } : {},
|
|
713
|
+
...supportAdminHeaders(config)
|
|
714
|
+
},
|
|
715
|
+
body: JSON.stringify(p),
|
|
716
|
+
signal: AbortSignal.timeout(15e3)
|
|
717
|
+
});
|
|
718
|
+
if (!response.ok) return `failed: HTTP ${response.status}`;
|
|
719
|
+
return "sent";
|
|
720
|
+
};
|
|
721
|
+
const retried = await drainOutbox(doSend);
|
|
722
|
+
try {
|
|
723
|
+
const result = await doSend(payload);
|
|
724
|
+
return retried > 0 ? `sent (+ ${retried} retried from outbox)` : result;
|
|
725
|
+
} catch (err) {
|
|
726
|
+
await persistToOutbox(payload);
|
|
727
|
+
return `failed (queued to outbox): ${err instanceof Error ? err.message : String(err)}`;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
async function sendTelemetry(sinceHours = 24) {
|
|
731
|
+
const payload = await buildTelemetryPayload(sinceHours);
|
|
732
|
+
const upstream = await sendTelemetryUpstream(payload);
|
|
733
|
+
return { payload, upstream };
|
|
734
|
+
}
|
|
735
|
+
export {
|
|
736
|
+
buildTelemetryPayload,
|
|
737
|
+
isTelemetryEnabled,
|
|
738
|
+
sanitizeMetricsForUpload,
|
|
739
|
+
sendTelemetry,
|
|
740
|
+
sendTelemetryUpstream
|
|
741
|
+
};
|