@ascenda-one/agent-mcp 0.1.11 → 0.1.13

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +448 -90
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,7 +23,7 @@ Agent (Claude Code / Cursor / other MCP host)
23
23
  -> AscendaEventSender.sendSemanticSignal (@ascenda-one/tool-kit)
24
24
  -> Ascenda backend (POST /v1/tool-events, consentScope: semantic_work_signals)
25
25
  -> paired anonymous Ascenda user
26
- -> Weekly Loop trigger evaluation / work-map (asc-core-be)
26
+ -> Weekly Loop trigger evaluation / work-map (backend)
27
27
  ```
28
28
 
29
29
  Reuses the same pairing model as the hooks and IDE extensions — same `toolInstallationId` + `eventWriteToken`, same token file convention — but **does not mint its own tool type**. See `src/config.ts`: this process is host-agnostic (the same binary runs under Claude Code or Cursor), so it refuses to guess a prefix the way `ascenda-claude-hook`/`ascenda-codex-hook` do. Passing an id that already contains `:` (i.e. the exact value your existing pairing shows) reuses that pairing's token file; a bare id is rejected rather than silently minting a second, unpaired tool identity.
package/dist/cli.js CHANGED
@@ -246,7 +246,28 @@ var require_afterHours = __commonJS({
246
246
  "../packages/tool-kit/out/afterHours.js"(exports) {
247
247
  "use strict";
248
248
  Object.defineProperty(exports, "__esModule", { value: true });
249
+ exports.BUSINESS_DAY = void 0;
250
+ exports.isOutsideBusinessHours = isOutsideBusinessHours;
249
251
  exports.isAfterHours = isAfterHours;
252
+ exports.utcOffsetMinutesAt = utcOffsetMinutesAt;
253
+ exports.localHourAt = localHourAt;
254
+ exports.BUSINESS_DAY = {
255
+ /** 0 = Sunday … 6 = Saturday. */
256
+ days: [1, 2, 3, 4, 5],
257
+ start: "09:00",
258
+ end: "17:00"
259
+ };
260
+ function isOutsideBusinessHours(at = /* @__PURE__ */ new Date(), opts = {}) {
261
+ const days = opts.days ?? exports.BUSINESS_DAY.days;
262
+ if (!days.includes(at.getDay()))
263
+ return true;
264
+ const minutes = at.getHours() * 60 + at.getMinutes();
265
+ const start = parseTimeToMinutes(opts.start ?? exports.BUSINESS_DAY.start, 9 * 60);
266
+ const end = parseTimeToMinutes(opts.end ?? exports.BUSINESS_DAY.end, 17 * 60);
267
+ if (start >= end)
268
+ return false;
269
+ return minutes < start || minutes >= end;
270
+ }
250
271
  function isAfterHours(now = /* @__PURE__ */ new Date(), start = "19:00", end = "07:00") {
251
272
  const currentMinutes = now.getHours() * 60 + now.getMinutes();
252
273
  const startMinutes = parseTimeToMinutes(start, 19 * 60);
@@ -267,6 +288,13 @@ var require_afterHours = __commonJS({
267
288
  return fallback;
268
289
  return Math.max(0, Math.min(23, hours)) * 60 + Math.max(0, Math.min(59, minutes));
269
290
  }
291
+ function utcOffsetMinutesAt(at) {
292
+ return -at.getTimezoneOffset();
293
+ }
294
+ function localHourAt(at, utcOffsetMinutes) {
295
+ const shifted = new Date(at.getTime() + utcOffsetMinutes * 6e4);
296
+ return shifted.getUTCHours();
297
+ }
270
298
  }
271
299
  });
272
300
 
@@ -358,7 +386,7 @@ var require_out = __commonJS({
358
386
  "../packages/tool-contract/out/index.js"(exports) {
359
387
  "use strict";
360
388
  Object.defineProperty(exports, "__esModule", { value: true });
361
- exports.ASCENDA_SEMANTIC_PROVENANCE = exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = exports.ASCENDA_PROVENANCE = exports.ASCENDA_CONSENT_SCOPE = exports.EVENT_WORKLOAD_CATEGORY = exports.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = void 0;
389
+ 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.COLLABORATION_EVENT_TYPES = exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = void 0;
362
390
  exports.SEMANTIC_WORK_SIGNAL_EVENT_TYPES = [
363
391
  "approach_churn_detected",
364
392
  "goal_drift_detected",
@@ -415,6 +443,7 @@ var require_out = __commonJS({
415
443
  exports.ASCENDA_PROVENANCE = "ai_work_telemetry";
416
444
  exports.ASCENDA_SEMANTIC_CONSENT_SCOPE = "semantic_work_signals";
417
445
  exports.ASCENDA_COLLABORATION_CONSENT_SCOPE = "workflow_telemetry";
446
+ exports.ASCENDA_HISTORICAL_CONSENT_SCOPE = "historical_import";
418
447
  exports.ASCENDA_SEMANTIC_PROVENANCE = "semantic_work_signals";
419
448
  }
420
449
  });
@@ -820,6 +849,7 @@ var require_eventSender = __commonJS({
820
849
  Object.defineProperty(exports, "__esModule", { value: true });
821
850
  exports.AscendaEventSender = exports.AscendaSemanticEventError = void 0;
822
851
  exports.buildEventPayload = buildEventPayload;
852
+ var afterHours_1 = require_afterHours();
823
853
  var tool_contract_1 = require_out();
824
854
  var eventLog_1 = require_eventLog();
825
855
  var http_1 = require_http();
@@ -837,8 +867,10 @@ var require_eventSender = __commonJS({
837
867
  toolInstallationId: identity.toolInstallationId,
838
868
  source: identity.source,
839
869
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
870
+ utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
840
871
  sessionId: identity.sessionId ?? void 0,
841
872
  workspaceHash: identity.workspaceHash ?? void 0,
873
+ projectHash: identity.projectHash ?? void 0,
842
874
  consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
843
875
  provenance: tool_contract_1.ASCENDA_PROVENANCE,
844
876
  privacyMode: "metadata_only",
@@ -890,9 +922,11 @@ var require_eventSender = __commonJS({
890
922
  source: this.config.source,
891
923
  eventType: mapped.eventType,
892
924
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
925
+ utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
893
926
  severity: "low",
894
927
  sessionId: this.config.sessionId ?? void 0,
895
928
  workspaceHash: this.config.workspaceHash ?? void 0,
929
+ projectHash: this.config.projectHash ?? void 0,
896
930
  consentScope: tool_contract_1.ASCENDA_SEMANTIC_CONSENT_SCOPE,
897
931
  provenance: tool_contract_1.ASCENDA_SEMANTIC_PROVENANCE,
898
932
  privacyMode: "metadata_only",
@@ -918,9 +952,11 @@ var require_eventSender = __commonJS({
918
952
  source: this.config.source,
919
953
  eventType: mapped.eventType,
920
954
  occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
955
+ utcOffsetMinutes: (0, afterHours_1.utcOffsetMinutesAt)(/* @__PURE__ */ new Date()),
921
956
  severity: "low",
922
957
  sessionId: this.config.sessionId ?? void 0,
923
958
  workspaceHash: this.config.workspaceHash ?? void 0,
959
+ projectHash: this.config.projectHash ?? void 0,
924
960
  consentScope: tool_contract_1.ASCENDA_COLLABORATION_CONSENT_SCOPE,
925
961
  provenance: tool_contract_1.ASCENDA_PROVENANCE,
926
962
  privacyMode: "metadata_only",
@@ -1011,19 +1047,387 @@ var require_eventSender = __commonJS({
1011
1047
  }
1012
1048
  });
1013
1049
 
1050
+ // ../packages/tool-kit/out/contextRegistry.js
1051
+ var require_contextRegistry = __commonJS({
1052
+ "../packages/tool-kit/out/contextRegistry.js"(exports) {
1053
+ "use strict";
1054
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
1055
+ if (k2 === void 0) k2 = k;
1056
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1057
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1058
+ desc = { enumerable: true, get: function() {
1059
+ return m[k];
1060
+ } };
1061
+ }
1062
+ Object.defineProperty(o, k2, desc);
1063
+ } : function(o, m, k, k2) {
1064
+ if (k2 === void 0) k2 = k;
1065
+ o[k2] = m[k];
1066
+ });
1067
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
1068
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1069
+ } : function(o, v) {
1070
+ o["default"] = v;
1071
+ });
1072
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
1073
+ var ownKeys = function(o) {
1074
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
1075
+ var ar = [];
1076
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1077
+ return ar;
1078
+ };
1079
+ return ownKeys(o);
1080
+ };
1081
+ return function(mod) {
1082
+ if (mod && mod.__esModule) return mod;
1083
+ var result = {};
1084
+ if (mod != null) {
1085
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1086
+ }
1087
+ __setModuleDefault(result, mod);
1088
+ return result;
1089
+ };
1090
+ }();
1091
+ Object.defineProperty(exports, "__esModule", { value: true });
1092
+ exports.workContextRegistryFilePath = workContextRegistryFilePath;
1093
+ exports.readWorkContextRegistry = readWorkContextRegistry;
1094
+ exports.recordWorkContext = recordWorkContext;
1095
+ exports.recordWorkContextAlias = recordWorkContextAlias;
1096
+ var fs = __importStar(__require("fs"));
1097
+ var os = __importStar(__require("os"));
1098
+ var path = __importStar(__require("path"));
1099
+ var MAX_PATHS_PER_ENTRY = 8;
1100
+ function workContextRegistryFilePath() {
1101
+ return path.join(os.homedir(), ".ascenda", "work-contexts.json");
1102
+ }
1103
+ function readWorkContextRegistry(registryFilePath = workContextRegistryFilePath()) {
1104
+ try {
1105
+ const parsed = JSON.parse(fs.readFileSync(registryFilePath, "utf8"));
1106
+ if (parsed && parsed.version === 1 && parsed.contexts && typeof parsed.contexts === "object") {
1107
+ return parsed;
1108
+ }
1109
+ } catch {
1110
+ }
1111
+ return { version: 1, contexts: {} };
1112
+ }
1113
+ function recordWorkContext(context, options) {
1114
+ if (!context)
1115
+ return false;
1116
+ const updates = [];
1117
+ if (context.projectHash && context.projectLabel) {
1118
+ updates.push({ hash: context.projectHash, kind: "project", label: context.projectLabel, observedPath: context.projectPath ?? context.workspacePath });
1119
+ }
1120
+ if (context.workspaceHash && context.workspaceLabel && context.workspaceHash !== context.projectHash) {
1121
+ updates.push({ hash: context.workspaceHash, kind: "workspace", label: context.workspaceLabel, observedPath: context.workspacePath });
1122
+ }
1123
+ return upsert(updates, options);
1124
+ }
1125
+ function recordWorkContextAlias(hash, label, observedPath, options) {
1126
+ if (!hash || !label)
1127
+ return false;
1128
+ return upsert([{ hash, kind: "alias", label, observedPath: observedPath ?? null }], options);
1129
+ }
1130
+ function upsert(updates, options) {
1131
+ if (updates.length === 0)
1132
+ return false;
1133
+ try {
1134
+ const registryFilePath = options?.registryFilePath ?? workContextRegistryFilePath();
1135
+ const nowIso = (options?.now ?? /* @__PURE__ */ new Date()).toISOString();
1136
+ const registry2 = readWorkContextRegistry(registryFilePath);
1137
+ let dirty = false;
1138
+ for (const update of updates) {
1139
+ const existing = registry2.contexts[update.hash];
1140
+ if (!existing) {
1141
+ registry2.contexts[update.hash] = {
1142
+ kind: update.kind,
1143
+ label: update.label,
1144
+ paths: update.observedPath ? [update.observedPath] : [],
1145
+ firstSeenAt: nowIso,
1146
+ lastSeenAt: nowIso
1147
+ };
1148
+ dirty = true;
1149
+ continue;
1150
+ }
1151
+ if (existing.kind === "alias" && update.kind !== "alias") {
1152
+ existing.kind = update.kind;
1153
+ dirty = true;
1154
+ }
1155
+ if (existing.label !== update.label && update.kind !== "alias") {
1156
+ existing.label = update.label;
1157
+ dirty = true;
1158
+ }
1159
+ if (update.observedPath && !existing.paths.includes(update.observedPath)) {
1160
+ if (existing.paths.length < MAX_PATHS_PER_ENTRY)
1161
+ existing.paths.push(update.observedPath);
1162
+ dirty = true;
1163
+ }
1164
+ if (dayOf(existing.lastSeenAt) !== dayOf(nowIso)) {
1165
+ existing.lastSeenAt = nowIso;
1166
+ dirty = true;
1167
+ }
1168
+ }
1169
+ if (!dirty)
1170
+ return false;
1171
+ writeRegistry(registryFilePath, registry2);
1172
+ return true;
1173
+ } catch {
1174
+ return false;
1175
+ }
1176
+ }
1177
+ function dayOf(iso) {
1178
+ return iso.slice(0, 10);
1179
+ }
1180
+ function writeRegistry(registryFilePath, registry2) {
1181
+ const dir = path.dirname(registryFilePath);
1182
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
1183
+ const tmp = `${registryFilePath}.${process.pid}.tmp`;
1184
+ fs.writeFileSync(tmp, `${JSON.stringify(registry2, null, 2)}
1185
+ `, { encoding: "utf8", mode: 384 });
1186
+ fs.renameSync(tmp, registryFilePath);
1187
+ if (process.platform !== "win32") {
1188
+ fs.chmodSync(registryFilePath, 384);
1189
+ }
1190
+ }
1191
+ }
1192
+ });
1193
+
1194
+ // ../packages/tool-kit/out/salt.js
1195
+ var require_salt = __commonJS({
1196
+ "../packages/tool-kit/out/salt.js"(exports) {
1197
+ "use strict";
1198
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
1199
+ if (k2 === void 0) k2 = k;
1200
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1201
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1202
+ desc = { enumerable: true, get: function() {
1203
+ return m[k];
1204
+ } };
1205
+ }
1206
+ Object.defineProperty(o, k2, desc);
1207
+ } : function(o, m, k, k2) {
1208
+ if (k2 === void 0) k2 = k;
1209
+ o[k2] = m[k];
1210
+ });
1211
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
1212
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1213
+ } : function(o, v) {
1214
+ o["default"] = v;
1215
+ });
1216
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
1217
+ var ownKeys = function(o) {
1218
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
1219
+ var ar = [];
1220
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1221
+ return ar;
1222
+ };
1223
+ return ownKeys(o);
1224
+ };
1225
+ return function(mod) {
1226
+ if (mod && mod.__esModule) return mod;
1227
+ var result = {};
1228
+ if (mod != null) {
1229
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1230
+ }
1231
+ __setModuleDefault(result, mod);
1232
+ return result;
1233
+ };
1234
+ }();
1235
+ Object.defineProperty(exports, "__esModule", { value: true });
1236
+ exports.machineSaltFilePath = machineSaltFilePath;
1237
+ exports.readOrCreateMachineSalt = readOrCreateMachineSalt;
1238
+ exports.hashWithMachineSalt = hashWithMachineSalt;
1239
+ var crypto = __importStar(__require("crypto"));
1240
+ var fs = __importStar(__require("fs"));
1241
+ var os = __importStar(__require("os"));
1242
+ var path = __importStar(__require("path"));
1243
+ function machineSaltFilePath() {
1244
+ return path.join(os.homedir(), ".ascenda", "salt");
1245
+ }
1246
+ var cache = /* @__PURE__ */ new Map();
1247
+ function readOrCreateMachineSalt(saltFilePath = machineSaltFilePath()) {
1248
+ const hit = cache.get(saltFilePath);
1249
+ if (hit)
1250
+ return hit;
1251
+ const dir = path.dirname(saltFilePath);
1252
+ fs.mkdirSync(dir, { recursive: true, mode: 448 });
1253
+ let salt;
1254
+ try {
1255
+ salt = crypto.randomBytes(32).toString("hex");
1256
+ fs.writeFileSync(saltFilePath, salt, { encoding: "utf8", mode: 384, flag: "wx" });
1257
+ } catch (error2) {
1258
+ if (error2.code !== "EEXIST")
1259
+ throw error2;
1260
+ salt = fs.readFileSync(saltFilePath, "utf8").trim();
1261
+ }
1262
+ if (process.platform !== "win32") {
1263
+ fs.chmodSync(dir, 448);
1264
+ fs.chmodSync(saltFilePath, 384);
1265
+ }
1266
+ cache.set(saltFilePath, salt);
1267
+ return salt;
1268
+ }
1269
+ function hashWithMachineSalt(value, saltFilePath) {
1270
+ if (!value)
1271
+ return null;
1272
+ return crypto.createHash("sha256").update(readOrCreateMachineSalt(saltFilePath)).update("\0").update(value).digest("hex").slice(0, 16);
1273
+ }
1274
+ }
1275
+ });
1276
+
1277
+ // ../packages/tool-kit/out/workContext.js
1278
+ var require_workContext = __commonJS({
1279
+ "../packages/tool-kit/out/workContext.js"(exports) {
1280
+ "use strict";
1281
+ var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
1282
+ if (k2 === void 0) k2 = k;
1283
+ var desc = Object.getOwnPropertyDescriptor(m, k);
1284
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1285
+ desc = { enumerable: true, get: function() {
1286
+ return m[k];
1287
+ } };
1288
+ }
1289
+ Object.defineProperty(o, k2, desc);
1290
+ } : function(o, m, k, k2) {
1291
+ if (k2 === void 0) k2 = k;
1292
+ o[k2] = m[k];
1293
+ });
1294
+ var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
1295
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
1296
+ } : function(o, v) {
1297
+ o["default"] = v;
1298
+ });
1299
+ var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
1300
+ var ownKeys = function(o) {
1301
+ ownKeys = Object.getOwnPropertyNames || function(o2) {
1302
+ var ar = [];
1303
+ for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1304
+ return ar;
1305
+ };
1306
+ return ownKeys(o);
1307
+ };
1308
+ return function(mod) {
1309
+ if (mod && mod.__esModule) return mod;
1310
+ var result = {};
1311
+ if (mod != null) {
1312
+ for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1313
+ }
1314
+ __setModuleDefault(result, mod);
1315
+ return result;
1316
+ };
1317
+ }();
1318
+ Object.defineProperty(exports, "__esModule", { value: true });
1319
+ exports.deriveWorkContext = deriveWorkContext;
1320
+ var fs = __importStar(__require("fs"));
1321
+ var path = __importStar(__require("path"));
1322
+ var salt_1 = require_salt();
1323
+ var MAX_WALK_DEPTH = 64;
1324
+ function deriveWorkContext(cwd, saltFilePath) {
1325
+ if (!cwd || !cwd.trim())
1326
+ return null;
1327
+ const startPath = stripTrailingSeparators(cwd.trim());
1328
+ let roots = null;
1329
+ try {
1330
+ roots = resolveRepositoryRoots(startPath);
1331
+ } catch {
1332
+ roots = null;
1333
+ }
1334
+ const workspacePath = roots?.checkoutRoot ?? startPath;
1335
+ const workspaceLabel = basenameOf(workspacePath);
1336
+ if (!workspaceLabel)
1337
+ return null;
1338
+ const projectPath = roots?.canonicalRoot ?? null;
1339
+ const projectLabel = (projectPath ? basenameOf(projectPath) : null) ?? workspaceLabel;
1340
+ return {
1341
+ workspaceLabel,
1342
+ projectLabel,
1343
+ workspaceHash: (0, salt_1.hashWithMachineSalt)(workspaceLabel, saltFilePath),
1344
+ projectHash: (0, salt_1.hashWithMachineSalt)(projectLabel, saltFilePath),
1345
+ workspacePath,
1346
+ projectPath
1347
+ };
1348
+ }
1349
+ function resolveRepositoryRoots(startDir) {
1350
+ if (!fs.existsSync(startDir))
1351
+ return null;
1352
+ let dir = startDir;
1353
+ for (let depth = 0; depth < MAX_WALK_DEPTH; depth++) {
1354
+ const dotGit = path.join(dir, ".git");
1355
+ let stat = null;
1356
+ try {
1357
+ stat = fs.statSync(dotGit);
1358
+ } catch {
1359
+ stat = null;
1360
+ }
1361
+ if (stat?.isDirectory())
1362
+ return { checkoutRoot: dir, canonicalRoot: dir };
1363
+ if (stat?.isFile())
1364
+ return { checkoutRoot: dir, canonicalRoot: worktreeParentRoot(dotGit, dir) ?? dir };
1365
+ const parent = path.dirname(dir);
1366
+ if (parent === dir)
1367
+ return null;
1368
+ dir = parent;
1369
+ }
1370
+ return null;
1371
+ }
1372
+ function worktreeParentRoot(dotGitFile, containingDir) {
1373
+ let gitdir;
1374
+ try {
1375
+ const match = /^gitdir:\s*(.+)\s*$/m.exec(fs.readFileSync(dotGitFile, "utf8"));
1376
+ if (!match)
1377
+ return null;
1378
+ gitdir = match[1].trim();
1379
+ } catch {
1380
+ return null;
1381
+ }
1382
+ const resolved = path.resolve(containingDir, gitdir);
1383
+ const marker = `${path.sep}.git${path.sep}worktrees${path.sep}`;
1384
+ const idx = resolved.indexOf(marker);
1385
+ if (idx === -1)
1386
+ return null;
1387
+ return resolved.slice(0, idx);
1388
+ }
1389
+ function stripTrailingSeparators(value) {
1390
+ let end = value.length;
1391
+ while (end > 1 && (value[end - 1] === "/" || value[end - 1] === "\\"))
1392
+ end--;
1393
+ return value.slice(0, end);
1394
+ }
1395
+ function basenameOf(value) {
1396
+ const segment = value.split(/[\\/]/).filter(Boolean).pop() ?? null;
1397
+ return segment && segment.length > 0 ? segment : null;
1398
+ }
1399
+ }
1400
+ });
1401
+
1014
1402
  // ../packages/tool-kit/out/hookAdapter.js
1015
1403
  var require_hookAdapter = __commonJS({
1016
1404
  "../packages/tool-kit/out/hookAdapter.js"(exports) {
1017
1405
  "use strict";
1018
1406
  Object.defineProperty(exports, "__esModule", { value: true });
1019
1407
  exports.DEFAULT_API_BASE_URL = void 0;
1408
+ exports.resolveContextHashes = resolveContextHashes;
1020
1409
  exports.loadCliAgentConfig = loadCliAgentConfig;
1021
1410
  exports.deliverHookEvents = deliverHookEvents;
1411
+ var contextRegistry_1 = require_contextRegistry();
1022
1412
  var eventLog_1 = require_eventLog();
1023
1413
  var eventSender_1 = require_eventSender();
1024
1414
  var tokenStore_1 = require_tokenStore();
1415
+ var workContext_1 = require_workContext();
1025
1416
  exports.DEFAULT_API_BASE_URL = "https://api.ascenda.one";
1026
- function loadCliAgentConfig(toolType, sessionIdFromHook) {
1417
+ function resolveContextHashes(cwd) {
1418
+ const workspaceOverride = process.env.ASCENDA_WORKSPACE_HASH?.trim() || null;
1419
+ const projectOverride = process.env.ASCENDA_PROJECT_HASH?.trim() || null;
1420
+ if (workspaceOverride && projectOverride)
1421
+ return { workspaceHash: workspaceOverride, projectHash: projectOverride };
1422
+ const context = (0, workContext_1.deriveWorkContext)(cwd ?? process.cwd());
1423
+ if (context)
1424
+ (0, contextRegistry_1.recordWorkContext)(context);
1425
+ return {
1426
+ workspaceHash: workspaceOverride ?? context?.workspaceHash ?? null,
1427
+ projectHash: projectOverride ?? context?.projectHash ?? null
1428
+ };
1429
+ }
1430
+ function loadCliAgentConfig(toolType, sessionIdFromHook, cwd) {
1027
1431
  const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? exports.DEFAULT_API_BASE_URL).replace(/\/$/, "");
1028
1432
  const toolInstallationIdRaw = process.env.ASCENDA_TOOL_INSTALLATION_ID;
1029
1433
  if (!toolInstallationIdRaw)
@@ -1036,13 +1440,15 @@ var require_hookAdapter = __commonJS({
1036
1440
  throw new Error("Missing ASCENDA_EVENT_WRITE_TOKEN (or token file)");
1037
1441
  if (!fileToken)
1038
1442
  (0, tokenStore_1.persistEventWriteToken)(tokenFilePath, eventWriteToken);
1443
+ const contextHashes = resolveContextHashes(cwd);
1039
1444
  return {
1040
1445
  apiBaseUrl,
1041
1446
  toolInstallationId,
1042
1447
  eventWriteToken,
1043
1448
  tokenFilePath,
1044
1449
  sessionId: process.env.ASCENDA_SESSION_ID ?? sessionIdFromHook ?? null,
1045
- workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null,
1450
+ workspaceHash: contextHashes.workspaceHash,
1451
+ projectHash: contextHashes.projectHash,
1046
1452
  // Agents await command hooks; fail fast rather than stall the user's turn.
1047
1453
  timeoutMs: parsePositiveInt(process.env.ASCENDA_HTTP_TIMEOUT_MS) ?? 3e3
1048
1454
  };
@@ -1053,11 +1459,12 @@ var require_hookAdapter = __commonJS({
1053
1459
  const notice = options.onNotice ?? ((message) => console.error(message));
1054
1460
  let config2;
1055
1461
  try {
1056
- config2 = loadCliAgentConfig(options.toolType, options.sessionId);
1462
+ config2 = loadCliAgentConfig(options.toolType, options.sessionId, options.cwd);
1057
1463
  } catch (error2) {
1058
1464
  const logFile = (0, eventLog_1.resolveEventLogPath)();
1059
1465
  if (!logFile)
1060
1466
  throw error2;
1467
+ const contextHashes = resolveContextHashes(options.cwd);
1061
1468
  for (const event of events) {
1062
1469
  (0, eventLog_1.appendEventLog)(logFile, {
1063
1470
  loggedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -1066,7 +1473,8 @@ var require_hookAdapter = __commonJS({
1066
1473
  toolInstallationId: `${options.toolType}:unpaired`,
1067
1474
  source: options.source,
1068
1475
  sessionId: options.sessionId ?? null,
1069
- workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null
1476
+ workspaceHash: contextHashes.workspaceHash,
1477
+ projectHash: contextHashes.projectHash
1070
1478
  }, event)
1071
1479
  });
1072
1480
  }
@@ -1080,6 +1488,7 @@ var require_hookAdapter = __commonJS({
1080
1488
  tokenFilePath: config2.tokenFilePath,
1081
1489
  sessionId: config2.sessionId,
1082
1490
  workspaceHash: config2.workspaceHash,
1491
+ projectHash: config2.projectHash,
1083
1492
  timeoutMs: config2.timeoutMs
1084
1493
  });
1085
1494
  for (const event of events) {
@@ -1185,89 +1594,6 @@ var require_turnState = __commonJS({
1185
1594
  }
1186
1595
  });
1187
1596
 
1188
- // ../packages/tool-kit/out/salt.js
1189
- var require_salt = __commonJS({
1190
- "../packages/tool-kit/out/salt.js"(exports) {
1191
- "use strict";
1192
- var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
1193
- if (k2 === void 0) k2 = k;
1194
- var desc = Object.getOwnPropertyDescriptor(m, k);
1195
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
1196
- desc = { enumerable: true, get: function() {
1197
- return m[k];
1198
- } };
1199
- }
1200
- Object.defineProperty(o, k2, desc);
1201
- } : function(o, m, k, k2) {
1202
- if (k2 === void 0) k2 = k;
1203
- o[k2] = m[k];
1204
- });
1205
- var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
1206
- Object.defineProperty(o, "default", { enumerable: true, value: v });
1207
- } : function(o, v) {
1208
- o["default"] = v;
1209
- });
1210
- var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
1211
- var ownKeys = function(o) {
1212
- ownKeys = Object.getOwnPropertyNames || function(o2) {
1213
- var ar = [];
1214
- for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
1215
- return ar;
1216
- };
1217
- return ownKeys(o);
1218
- };
1219
- return function(mod) {
1220
- if (mod && mod.__esModule) return mod;
1221
- var result = {};
1222
- if (mod != null) {
1223
- for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
1224
- }
1225
- __setModuleDefault(result, mod);
1226
- return result;
1227
- };
1228
- }();
1229
- Object.defineProperty(exports, "__esModule", { value: true });
1230
- exports.machineSaltFilePath = machineSaltFilePath;
1231
- exports.readOrCreateMachineSalt = readOrCreateMachineSalt;
1232
- exports.hashWithMachineSalt = hashWithMachineSalt;
1233
- var crypto = __importStar(__require("crypto"));
1234
- var fs = __importStar(__require("fs"));
1235
- var os = __importStar(__require("os"));
1236
- var path = __importStar(__require("path"));
1237
- function machineSaltFilePath() {
1238
- return path.join(os.homedir(), ".ascenda", "salt");
1239
- }
1240
- var cache = /* @__PURE__ */ new Map();
1241
- function readOrCreateMachineSalt(saltFilePath = machineSaltFilePath()) {
1242
- const hit = cache.get(saltFilePath);
1243
- if (hit)
1244
- return hit;
1245
- const dir = path.dirname(saltFilePath);
1246
- fs.mkdirSync(dir, { recursive: true, mode: 448 });
1247
- let salt;
1248
- try {
1249
- salt = crypto.randomBytes(32).toString("hex");
1250
- fs.writeFileSync(saltFilePath, salt, { encoding: "utf8", mode: 384, flag: "wx" });
1251
- } catch (error2) {
1252
- if (error2.code !== "EEXIST")
1253
- throw error2;
1254
- salt = fs.readFileSync(saltFilePath, "utf8").trim();
1255
- }
1256
- if (process.platform !== "win32") {
1257
- fs.chmodSync(dir, 448);
1258
- fs.chmodSync(saltFilePath, 384);
1259
- }
1260
- cache.set(saltFilePath, salt);
1261
- return salt;
1262
- }
1263
- function hashWithMachineSalt(value, saltFilePath) {
1264
- if (!value)
1265
- return null;
1266
- return crypto.createHash("sha256").update(readOrCreateMachineSalt(saltFilePath)).update("\0").update(value).digest("hex").slice(0, 16);
1267
- }
1268
- }
1269
- });
1270
-
1271
1597
  // ../packages/tool-kit/out/liveBus.js
1272
1598
  var require_liveBus = __commonJS({
1273
1599
  "../packages/tool-kit/out/liveBus.js"(exports) {
@@ -1399,8 +1725,8 @@ var require_out2 = __commonJS({
1399
1725
  "../packages/tool-kit/out/index.js"(exports) {
1400
1726
  "use strict";
1401
1727
  Object.defineProperty(exports, "__esModule", { value: true });
1402
- exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.defaultTokenFilePath = exports.recordTurnStart = exports.consumeTurnDurationMs = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = exports.looksLikeCorrection = exports.outcomeForHook = exports.inferOutcome = exports.getNestedNumber = exports.getNestedString = exports.getNested = exports.getNumber = exports.getString = exports.isAfterHours = exports.bucketDurationMs = exports.bucketLinesChanged = exports.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
1403
- exports.isRetryableStatus = exports.parseIngestResponse = void 0;
1728
+ exports.workContextRegistryFilePath = exports.readWorkContextRegistry = exports.recordWorkContextAlias = exports.recordWorkContext = exports.deriveWorkContext = exports.hashWithMachineSalt = exports.readOrCreateMachineSalt = exports.machineSaltFilePath = exports.markFailureNotified = exports.shouldAnnounceFailure = exports.recordSendOutcome = exports.readCollectorState = exports.defaultStateFilePath = exports.readTokenFile = exports.persistEventWriteToken = exports.defaultTokenFilePath = exports.recordTurnStart = exports.consumeTurnDurationMs = exports.resolveContextHashes = exports.loadCliAgentConfig = exports.deliverHookEvents = exports.DEFAULT_API_BASE_URL = exports.resolveEventLogPath = exports.expandUserPath = exports.appendEventLog = exports.EVENT_LOG_ENV_VAR = exports.buildEventPayload = exports.AscendaSemanticEventError = exports.AscendaEventSender = 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.invitesDebrief = exports.classifyWorkMilestone = exports.isReworkGitAction = exports.classifyGitAction = exports.isVerificationCommand = exports.classifyCommand = void 0;
1729
+ exports.isRetryableStatus = exports.parseIngestResponse = exports.postToolEventsBatch = exports.postToolEvent = exports.renewToolToken = exports.getPairingStatus = exports.createPairingSession = exports.AscendaApiError = exports.liveBusSocketCandidates = exports.liveBusSocketPath = exports.bucketPromptSize = exports.emitLiveSignal = void 0;
1404
1730
  var commandClassifier_1 = require_commandClassifier();
1405
1731
  Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
1406
1732
  return commandClassifier_1.classifyCommand;
@@ -1433,6 +1759,18 @@ var require_out2 = __commonJS({
1433
1759
  Object.defineProperty(exports, "isAfterHours", { enumerable: true, get: function() {
1434
1760
  return afterHours_1.isAfterHours;
1435
1761
  } });
1762
+ Object.defineProperty(exports, "isOutsideBusinessHours", { enumerable: true, get: function() {
1763
+ return afterHours_1.isOutsideBusinessHours;
1764
+ } });
1765
+ Object.defineProperty(exports, "BUSINESS_DAY", { enumerable: true, get: function() {
1766
+ return afterHours_1.BUSINESS_DAY;
1767
+ } });
1768
+ Object.defineProperty(exports, "utcOffsetMinutesAt", { enumerable: true, get: function() {
1769
+ return afterHours_1.utcOffsetMinutesAt;
1770
+ } });
1771
+ Object.defineProperty(exports, "localHourAt", { enumerable: true, get: function() {
1772
+ return afterHours_1.localHourAt;
1773
+ } });
1436
1774
  var payload_1 = require_payload();
1437
1775
  Object.defineProperty(exports, "getString", { enumerable: true, get: function() {
1438
1776
  return payload_1.getString;
@@ -1491,6 +1829,9 @@ var require_out2 = __commonJS({
1491
1829
  Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
1492
1830
  return hookAdapter_1.loadCliAgentConfig;
1493
1831
  } });
1832
+ Object.defineProperty(exports, "resolveContextHashes", { enumerable: true, get: function() {
1833
+ return hookAdapter_1.resolveContextHashes;
1834
+ } });
1494
1835
  var turnState_1 = require_turnState();
1495
1836
  Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
1496
1837
  return turnState_1.consumeTurnDurationMs;
@@ -1534,6 +1875,23 @@ var require_out2 = __commonJS({
1534
1875
  Object.defineProperty(exports, "hashWithMachineSalt", { enumerable: true, get: function() {
1535
1876
  return salt_1.hashWithMachineSalt;
1536
1877
  } });
1878
+ var workContext_1 = require_workContext();
1879
+ Object.defineProperty(exports, "deriveWorkContext", { enumerable: true, get: function() {
1880
+ return workContext_1.deriveWorkContext;
1881
+ } });
1882
+ var contextRegistry_1 = require_contextRegistry();
1883
+ Object.defineProperty(exports, "recordWorkContext", { enumerable: true, get: function() {
1884
+ return contextRegistry_1.recordWorkContext;
1885
+ } });
1886
+ Object.defineProperty(exports, "recordWorkContextAlias", { enumerable: true, get: function() {
1887
+ return contextRegistry_1.recordWorkContextAlias;
1888
+ } });
1889
+ Object.defineProperty(exports, "readWorkContextRegistry", { enumerable: true, get: function() {
1890
+ return contextRegistry_1.readWorkContextRegistry;
1891
+ } });
1892
+ Object.defineProperty(exports, "workContextRegistryFilePath", { enumerable: true, get: function() {
1893
+ return contextRegistry_1.workContextRegistryFilePath;
1894
+ } });
1537
1895
  var liveBus_1 = require_liveBus();
1538
1896
  Object.defineProperty(exports, "emitLiveSignal", { enumerable: true, get: function() {
1539
1897
  return liveBus_1.emitLiveSignal;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "publishConfig": { "access": "public" },
3
3
  "name": "@ascenda-one/agent-mcp",
4
- "version": "0.1.11",
4
+ "version": "0.1.13",
5
5
  "description": "MCP server exposing ascenda_emit_work_signal — the one submission interface for agent-observed (semantic) work-friction signals.",
6
6
  "repository": {
7
7
  "type": "git",