@ascenda-one/cursor-hooks 0.1.18 → 0.1.20

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.
Files changed (2) hide show
  1. package/dist/cli.js +98 -12
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -583,7 +583,61 @@ var require_out = __commonJS({
583
583
  "../packages/tool-contract/out/index.js"(exports) {
584
584
  "use strict";
585
585
  Object.defineProperty(exports, "__esModule", { value: true });
586
- exports.backendMetricKeys = exports.METRIC_KEYS = exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.TOOL_EVENT_DELIVERED_STATUSES = exports.IDEMPOTENCY_KEY_MAX_LENGTH = exports.EVENT_METADATA_FIELDS = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = void 0;
586
+ exports.backendMetricKeys = exports.METRIC_KEYS = exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.TOOL_EVENT_DELIVERED_STATUSES = exports.IDEMPOTENCY_KEY_MAX_LENGTH = exports.EVENT_METADATA_FIELDS = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = exports.ASCENDA_ELAPSED_QUANTITIES = exports.ASCENDA_ACTIVE_TIME_QUANTITIES = exports.ASCENDA_TOOL_TYPES = exports.ASCENDA_TELEMETRY_SOURCES = void 0;
587
+ exports.combineDisjointHalves = combineDisjointHalves;
588
+ exports.requireComparableQuantities = requireComparableQuantities;
589
+ exports.ASCENDA_TELEMETRY_SOURCES = [
590
+ "vscode_extension",
591
+ "cursor_mcp",
592
+ "claude_code",
593
+ "copilot_otel",
594
+ "cli_agent",
595
+ "mcp_server",
596
+ "activity_signals",
597
+ "code_forge"
598
+ ];
599
+ exports.ASCENDA_TOOL_TYPES = [
600
+ "vscode_extension",
601
+ "cursor_mcp",
602
+ "claude_code",
603
+ "copilot_otel",
604
+ "cli_agent",
605
+ "mcp_server",
606
+ "github_collector",
607
+ "other"
608
+ ];
609
+ exports.ASCENDA_ACTIVE_TIME_QUANTITIES = [
610
+ "coverage",
611
+ "hands_on",
612
+ "supervising",
613
+ "block_coverage",
614
+ "hands_on_agent_hours",
615
+ "supervising_agent_hours"
616
+ ];
617
+ exports.ASCENDA_ELAPSED_QUANTITIES = {
618
+ coverage: true,
619
+ hands_on: true,
620
+ supervising: true,
621
+ block_coverage: true,
622
+ hands_on_agent_hours: false,
623
+ supervising_agent_hours: false
624
+ };
625
+ var DISJOINT_HALVES = [
626
+ ["hands_on", "supervising", "coverage"],
627
+ ["supervising", "hands_on", "coverage"]
628
+ ];
629
+ function combineDisjointHalves(left, leftQuantity, right, rightQuantity) {
630
+ const pair = DISJOINT_HALVES.find(([a, b]) => a === leftQuantity && b === rightQuantity);
631
+ if (!pair) {
632
+ throw new Error(`'combine-disjoint' combines active-time quantities '${leftQuantity}' and '${rightQuantity}', which do not partition a common whole.`);
633
+ }
634
+ return { total: left + right, quantity: pair[2] };
635
+ }
636
+ function requireComparableQuantities(left, right, operation) {
637
+ if (left !== right) {
638
+ throw new Error(`'${operation}' combines active-time quantities '${left}' and '${right}', which measure different things. See docs/ACTIVE_TIME.md, 'The quantity is the label, not the gap'.`);
639
+ }
640
+ }
587
641
  exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = [
588
642
  "approach_churn_detected",
589
643
  "goal_drift_detected",
@@ -1167,12 +1221,11 @@ var require_stateStore = __commonJS({
1167
1221
  exports.shouldAnnounceFailure = shouldAnnounceFailure;
1168
1222
  exports.markFailureNotified = markFailureNotified;
1169
1223
  var fs = __importStar(__require("fs"));
1170
- var os2 = __importStar(__require("os"));
1171
1224
  var path2 = __importStar(__require("path"));
1172
1225
  var tokenStore_1 = require_tokenStore();
1173
1226
  function defaultStateFilePath(toolInstallationId) {
1174
1227
  const dir = process.env.ASCENDA_STATE_DIR?.trim();
1175
- const base = dir ? dir : path2.join(os2.homedir(), ".ascenda", "state");
1228
+ const base = dir ? dir : path2.join((0, tokenStore_1.ascendaHome)(), "state");
1176
1229
  return path2.join(base, `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
1177
1230
  }
1178
1231
  function unresolvedToolInstallationId(toolType) {
@@ -1494,6 +1547,25 @@ var require_outbox = __commonJS({
1494
1547
  }
1495
1548
  });
1496
1549
 
1550
+ // ../packages/tool-kit/out/timeProvider.js
1551
+ var require_timeProvider = __commonJS({
1552
+ "../packages/tool-kit/out/timeProvider.js"(exports) {
1553
+ "use strict";
1554
+ Object.defineProperty(exports, "__esModule", { value: true });
1555
+ exports.systemTimeProvider = void 0;
1556
+ exports.fixedTimeProvider = fixedTimeProvider;
1557
+ exports.systemTimeProvider = {
1558
+ now: () => Date.now()
1559
+ };
1560
+ function fixedTimeProvider(instant) {
1561
+ const ms = typeof instant === "number" ? instant : new Date(instant).valueOf();
1562
+ if (!Number.isFinite(ms))
1563
+ throw new TypeError(`fixedTimeProvider: not a usable instant: ${String(instant)}`);
1564
+ return { now: () => ms };
1565
+ }
1566
+ }
1567
+ });
1568
+
1497
1569
  // ../packages/tool-kit/out/eventSender.js
1498
1570
  var require_eventSender = __commonJS({
1499
1571
  "../packages/tool-kit/out/eventSender.js"(exports) {
@@ -1506,6 +1578,7 @@ var require_eventSender = __commonJS({
1506
1578
  var eventLog_1 = require_eventLog();
1507
1579
  var http_1 = require_http();
1508
1580
  var outbox_1 = require_outbox();
1581
+ var timeProvider_1 = require_timeProvider();
1509
1582
  var tokenStore_1 = require_tokenStore();
1510
1583
  var stateStore_1 = require_stateStore();
1511
1584
  var payload_1 = require_payload();
@@ -1542,9 +1615,12 @@ var require_eventSender = __commonJS({
1542
1615
  lastDrain;
1543
1616
  /** One outbox pass per sender, i.e. per hook process. The hook is on the user's critical path. */
1544
1617
  outboxServiced = false;
1618
+ /** Resolved once, so every clock read in one pass sees the same instant. */
1619
+ time;
1545
1620
  constructor(config) {
1546
1621
  this.config = config;
1547
1622
  this.eventWriteToken = config.eventWriteToken;
1623
+ this.time = config.timeProvider ?? timeProvider_1.systemTimeProvider;
1548
1624
  }
1549
1625
  async send(mapped) {
1550
1626
  return this.post(buildEventPayload(this.config, mapped));
@@ -1695,7 +1771,7 @@ var require_eventSender = __commonJS({
1695
1771
  * journal's detail says so instead of implying it was kept.
1696
1772
  */
1697
1773
  enqueue(payload) {
1698
- return (0, outbox_1.appendToOutbox)(this.outboxFilePath(), payload);
1774
+ return (0, outbox_1.appendToOutbox)(this.outboxFilePath(), payload, new Date(this.time.now()));
1699
1775
  }
1700
1776
  /**
1701
1777
  * One pass over the outbox: claim it, apply the bounds, and — when sending
@@ -1718,7 +1794,7 @@ var require_eventSender = __commonJS({
1718
1794
  return void 0;
1719
1795
  this.outboxServiced = true;
1720
1796
  const sendEnabled = this.config.outboxDrain ?? (0, outbox_1.outboxDrainEnabled)();
1721
- const claimed = (0, outbox_1.claimOutbox)(this.outboxFilePath());
1797
+ const claimed = (0, outbox_1.claimOutbox)(this.outboxFilePath(), this.time.now());
1722
1798
  if (!claimed) {
1723
1799
  this.lastDrain = { found: 0, discarded: 0, delivered: 0, remaining: 0, sendEnabled };
1724
1800
  return void 0;
@@ -1727,7 +1803,7 @@ var require_eventSender = __commonJS({
1727
1803
  const { kept, discarded } = (0, outbox_1.enforceOutboxBounds)(claimed.entries, {
1728
1804
  maxEntries: this.config.outboxMaxEntries ?? outbox_1.DEFAULT_OUTBOX_MAX_ENTRIES,
1729
1805
  maxAgeMs: this.config.outboxMaxAgeMs ?? outbox_1.DEFAULT_OUTBOX_MAX_AGE_MS
1730
- });
1806
+ }, this.time.now());
1731
1807
  if (claimed.unreadable > 0) {
1732
1808
  discarded.count += claimed.unreadable;
1733
1809
  discarded.reasons.unreadable = claimed.unreadable;
@@ -2596,10 +2672,10 @@ var require_hookAdapter = __commonJS({
2596
2672
  exports.MissingInstallationIdError = MissingInstallationIdError;
2597
2673
  function resolveCliAgentInstallationId(toolType, identity = {}) {
2598
2674
  const fromEnv = process.env.ASCENDA_TOOL_INSTALLATION_ID?.trim();
2599
- if (fromEnv)
2675
+ if (fromEnv && belongsToToolType(toolType, fromEnv))
2600
2676
  return { toolInstallationId: qualify(toolType, fromEnv), source: "env" };
2601
2677
  const fromCredentials = identity.host ? (0, credentials_1.readHostCredentials)(identity.host)?.toolInstallationId?.trim() : void 0;
2602
- if (fromCredentials)
2678
+ if (fromCredentials && belongsToToolType(toolType, fromCredentials))
2603
2679
  return { toolInstallationId: qualify(toolType, fromCredentials), source: "credentials" };
2604
2680
  const candidates = (0, tokenStore_1.listPersistedToolInstallationIds)(toolType);
2605
2681
  if (candidates.length === 1)
@@ -2609,6 +2685,9 @@ var require_hookAdapter = __commonJS({
2609
2685
  function defaultSetupCommand(host) {
2610
2686
  return host ? `npx @ascenda-one/${host.replace(/_cli$/, "")}-hooks setup` : "the agent's setup command";
2611
2687
  }
2688
+ function belongsToToolType(toolType, value) {
2689
+ return !value.includes(":") || value.startsWith(`${toolType}:`);
2690
+ }
2612
2691
  function qualify(toolType, value) {
2613
2692
  return value.includes(":") ? value : `${toolType}:${value}`;
2614
2693
  }
@@ -3149,9 +3228,9 @@ var require_turnState = __commonJS({
3149
3228
  exports.recordTurnStart = recordTurnStart2;
3150
3229
  exports.consumeTurnDurationMs = consumeTurnDurationMs2;
3151
3230
  var fs = __importStar(__require("fs"));
3152
- var os2 = __importStar(__require("os"));
3153
3231
  var path2 = __importStar(__require("path"));
3154
- var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path2.join(os2.homedir(), ".ascenda", "state");
3232
+ var tokenStore_1 = require_tokenStore();
3233
+ var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path2.join((0, tokenStore_1.ascendaHome)(), "state");
3155
3234
  function turnFile(agent, sessionId) {
3156
3235
  return path2.join(stateDir(), `${sanitize(agent)}-turn-${sanitize(sessionId)}`);
3157
3236
  }
@@ -3318,8 +3397,8 @@ var require_out2 = __commonJS({
3318
3397
  "use strict";
3319
3398
  Object.defineProperty(exports, "__esModule", { value: true });
3320
3399
  exports.consumeTurnDurationMs = exports.writeTopLevelCredentials = exports.writeMachineCredentials = exports.writeHostCredentials = exports.removeHostCredentials = exports.readMachineCredentials = exports.readHostCredentials = exports.credentialsFilePath = exports.writeHookSettings = exports.runCliAgentSetup = exports.isCliAgentManagementCommand = exports.findStaleHookCommands = exports.cliAgentHookBinPath = exports.resolveContextHashes = exports.resolveCliAgentInstallationId = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.MissingInstallationIdError = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.mintIdempotencyKey = exports.looksLikeCorrection = exports.outcomeForHook = exports.inferOutcome = exports.getNestedNumber = exports.getNestedString = exports.getNested = exports.getNumber = exports.getString = exports.localHourAt = exports.utcOffsetMinutesAt = exports.BUSINESS_DAY = exports.isOutsideBusinessHours = exports.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.classifyModelClass = exports.autonomyBand = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
3321
- exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.recordForgeProjectAlias = exports.forgeFullNameFromConfig = exports.readForgeFullName = exports.parseForgeFullName = exports.forgeProjectHash = exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.readBranchName = exports.normalizeBranchName = exports.deriveBranchHashForCwd = exports.deriveBranchHash = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.enforceOutboxBounds = exports.claimOutbox = exports.readOutboxSummary = exports.appendToOutbox = exports.defaultOutboxFilePath = exports.outboxDrainEnabled = exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = exports.recordOutboxDiscard = exports.unresolvedToolInstallationId = exports.unresolvedStateFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.listPersistedToolInstallationIds = exports.defaultTokenFilePath = exports.ascendaHome = exports.recordTurnStart = void 0;
3322
- exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = void 0;
3400
+ exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.recordForgeProjectAlias = exports.forgeFullNameFromConfig = exports.readForgeFullName = exports.parseForgeFullName = exports.forgeProjectHash = exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.readBranchName = exports.normalizeBranchName = exports.deriveBranchHashForCwd = exports.deriveBranchHash = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.fixedTimeProvider = exports.systemTimeProvider = exports.enforceOutboxBounds = exports.claimOutbox = exports.readOutboxSummary = exports.appendToOutbox = exports.defaultOutboxFilePath = exports.outboxDrainEnabled = exports.DEFAULT_OUTBOX_DRAIN_BATCH_SIZE = exports.DEFAULT_OUTBOX_MAX_AGE_MS = exports.DEFAULT_OUTBOX_MAX_ENTRIES = exports.OUTBOX_DRAIN_ENV_VAR = exports.recordOutboxDiscard = exports.unresolvedToolInstallationId = exports.unresolvedStateFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.listPersistedToolInstallationIds = exports.defaultTokenFilePath = exports.ascendaHome = exports.recordTurnStart = void 0;
3401
+ exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = void 0;
3323
3402
  var commandClassifier_1 = require_commandClassifier();
3324
3403
  Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
3325
3404
  return commandClassifier_1.classifyCommand;
@@ -3559,6 +3638,13 @@ var require_out2 = __commonJS({
3559
3638
  Object.defineProperty(exports, "enforceOutboxBounds", { enumerable: true, get: function() {
3560
3639
  return outbox_1.enforceOutboxBounds;
3561
3640
  } });
3641
+ var timeProvider_1 = require_timeProvider();
3642
+ Object.defineProperty(exports, "systemTimeProvider", { enumerable: true, get: function() {
3643
+ return timeProvider_1.systemTimeProvider;
3644
+ } });
3645
+ Object.defineProperty(exports, "fixedTimeProvider", { enumerable: true, get: function() {
3646
+ return timeProvider_1.fixedTimeProvider;
3647
+ } });
3562
3648
  var salt_1 = require_salt();
3563
3649
  Object.defineProperty(exports, "machineSaltFilePath", { enumerable: true, get: function() {
3564
3650
  return salt_1.machineSaltFilePath;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "publishConfig": { "access": "public" },
3
3
  "name": "@ascenda-one/cursor-hooks",
4
- "version": "0.1.18",
4
+ "version": "0.1.20",
5
5
  "description": "Cursor agent hooks adapter for Ascenda AI workload telemetry",
6
6
  "repository": {
7
7
  "type": "git",