@ascenda-one/agent-mcp 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +562 -46
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -419,6 +419,93 @@ var require_out = __commonJS({
|
|
|
419
419
|
}
|
|
420
420
|
});
|
|
421
421
|
|
|
422
|
+
// ../packages/tool-kit/out/eventLog.js
|
|
423
|
+
var require_eventLog = __commonJS({
|
|
424
|
+
"../packages/tool-kit/out/eventLog.js"(exports) {
|
|
425
|
+
"use strict";
|
|
426
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
427
|
+
if (k2 === void 0) k2 = k;
|
|
428
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
429
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
430
|
+
desc = { enumerable: true, get: function() {
|
|
431
|
+
return m[k];
|
|
432
|
+
} };
|
|
433
|
+
}
|
|
434
|
+
Object.defineProperty(o, k2, desc);
|
|
435
|
+
} : function(o, m, k, k2) {
|
|
436
|
+
if (k2 === void 0) k2 = k;
|
|
437
|
+
o[k2] = m[k];
|
|
438
|
+
});
|
|
439
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
440
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
441
|
+
} : function(o, v) {
|
|
442
|
+
o["default"] = v;
|
|
443
|
+
});
|
|
444
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
445
|
+
var ownKeys = function(o) {
|
|
446
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
447
|
+
var ar = [];
|
|
448
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
449
|
+
return ar;
|
|
450
|
+
};
|
|
451
|
+
return ownKeys(o);
|
|
452
|
+
};
|
|
453
|
+
return function(mod) {
|
|
454
|
+
if (mod && mod.__esModule) return mod;
|
|
455
|
+
var result = {};
|
|
456
|
+
if (mod != null) {
|
|
457
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
458
|
+
}
|
|
459
|
+
__setModuleDefault(result, mod);
|
|
460
|
+
return result;
|
|
461
|
+
};
|
|
462
|
+
}();
|
|
463
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
464
|
+
exports.EVENT_LOG_ENV_VAR = void 0;
|
|
465
|
+
exports.expandUserPath = expandUserPath;
|
|
466
|
+
exports.resolveEventLogPath = resolveEventLogPath;
|
|
467
|
+
exports.appendEventLog = appendEventLog;
|
|
468
|
+
var fs = __importStar(__require("fs"));
|
|
469
|
+
var os = __importStar(__require("os"));
|
|
470
|
+
var path = __importStar(__require("path"));
|
|
471
|
+
exports.EVENT_LOG_ENV_VAR = "ASCENDA_EVENT_LOG_FILE";
|
|
472
|
+
var MAX_BYTES = 5 * 1024 * 1024;
|
|
473
|
+
function expandUserPath(configured) {
|
|
474
|
+
const value = configured?.trim();
|
|
475
|
+
if (!value)
|
|
476
|
+
return void 0;
|
|
477
|
+
if (value === "~")
|
|
478
|
+
return os.homedir();
|
|
479
|
+
if (value.startsWith("~/"))
|
|
480
|
+
return path.join(os.homedir(), value.slice(2));
|
|
481
|
+
return path.resolve(value);
|
|
482
|
+
}
|
|
483
|
+
function resolveEventLogPath() {
|
|
484
|
+
return expandUserPath(process.env[exports.EVENT_LOG_ENV_VAR]);
|
|
485
|
+
}
|
|
486
|
+
function appendEventLog(logFilePath, entry) {
|
|
487
|
+
try {
|
|
488
|
+
rotateIfLarge(logFilePath);
|
|
489
|
+
const dir = path.dirname(logFilePath);
|
|
490
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
491
|
+
fs.appendFileSync(logFilePath, `${JSON.stringify(entry)}
|
|
492
|
+
`, { encoding: "utf8", mode: 384 });
|
|
493
|
+
if (process.platform !== "win32")
|
|
494
|
+
fs.chmodSync(logFilePath, 384);
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
function rotateIfLarge(logFilePath) {
|
|
499
|
+
try {
|
|
500
|
+
if (fs.statSync(logFilePath).size < MAX_BYTES)
|
|
501
|
+
return;
|
|
502
|
+
fs.renameSync(logFilePath, `${logFilePath}.1`);
|
|
503
|
+
} catch {
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
|
|
422
509
|
// ../packages/tool-kit/out/http.js
|
|
423
510
|
var require_http = __commonJS({
|
|
424
511
|
"../packages/tool-kit/out/http.js"(exports) {
|
|
@@ -428,6 +515,7 @@ var require_http = __commonJS({
|
|
|
428
515
|
exports.createPairingSession = createPairingSession;
|
|
429
516
|
exports.getPairingStatus = getPairingStatus;
|
|
430
517
|
exports.renewToolToken = renewToolToken;
|
|
518
|
+
exports.isRetryableStatus = isRetryableStatus;
|
|
431
519
|
exports.postToolEvent = postToolEvent;
|
|
432
520
|
exports.postToolEventsBatch = postToolEventsBatch;
|
|
433
521
|
exports.parseIngestResponse = parseIngestResponse;
|
|
@@ -473,26 +561,34 @@ var require_http = __commonJS({
|
|
|
473
561
|
throw new AscendaApiError(response.status, void 0, await response.text());
|
|
474
562
|
return await response.json();
|
|
475
563
|
}
|
|
564
|
+
function isRetryableStatus(status) {
|
|
565
|
+
return status === 408 || status === 429 || status !== void 0 && status >= 500 && status <= 599;
|
|
566
|
+
}
|
|
476
567
|
async function postToolEvent(apiBaseUrl, eventWriteToken, payload, signal) {
|
|
477
|
-
|
|
478
|
-
method: "POST",
|
|
479
|
-
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
480
|
-
body: JSON.stringify(payload),
|
|
481
|
-
signal
|
|
482
|
-
});
|
|
483
|
-
return parseIngestResponse(response);
|
|
568
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events`, eventWriteToken, JSON.stringify(payload), signal);
|
|
484
569
|
}
|
|
485
|
-
async function postToolEventsBatch(apiBaseUrl, eventWriteToken, payloads) {
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
570
|
+
async function postToolEventsBatch(apiBaseUrl, eventWriteToken, payloads, signal) {
|
|
571
|
+
return sendIngest(`${apiBaseUrl}/v1/tool-events/batch`, eventWriteToken, JSON.stringify({ events: payloads }), signal);
|
|
572
|
+
}
|
|
573
|
+
async function sendIngest(url, eventWriteToken, body, signal) {
|
|
574
|
+
try {
|
|
575
|
+
const response = await fetch(url, {
|
|
576
|
+
method: "POST",
|
|
577
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${eventWriteToken}` },
|
|
578
|
+
body,
|
|
579
|
+
signal
|
|
580
|
+
});
|
|
581
|
+
return parseIngestResponse(response);
|
|
582
|
+
} catch (error2) {
|
|
583
|
+
return {
|
|
584
|
+
result: "transport_error",
|
|
585
|
+
detail: error2 instanceof Error ? `${error2.name}: ${error2.message}` : String(error2)
|
|
586
|
+
};
|
|
587
|
+
}
|
|
492
588
|
}
|
|
493
589
|
async function parseIngestResponse(response) {
|
|
494
590
|
if (response.ok)
|
|
495
|
-
return "accepted";
|
|
591
|
+
return { result: "accepted", httpStatus: response.status };
|
|
496
592
|
const body = await response.text();
|
|
497
593
|
let errorCode;
|
|
498
594
|
try {
|
|
@@ -500,13 +596,14 @@ var require_http = __commonJS({
|
|
|
500
596
|
} catch {
|
|
501
597
|
errorCode = void 0;
|
|
502
598
|
}
|
|
599
|
+
const base = { httpStatus: response.status, errorCode, detail: body || void 0 };
|
|
503
600
|
if (response.status === 401)
|
|
504
|
-
return "auth_failed";
|
|
601
|
+
return { ...base, result: "auth_failed" };
|
|
505
602
|
if (response.status === 403 && errorCode === "consent_missing_or_expired")
|
|
506
|
-
return "consent_missing";
|
|
603
|
+
return { ...base, result: "consent_missing" };
|
|
507
604
|
if (response.status === 400 || response.status === 422)
|
|
508
|
-
return "validation_failed";
|
|
509
|
-
|
|
605
|
+
return { ...base, result: "validation_failed" };
|
|
606
|
+
return { ...base, result: "transport_error" };
|
|
510
607
|
}
|
|
511
608
|
}
|
|
512
609
|
});
|
|
@@ -556,6 +653,7 @@ var require_tokenStore = __commonJS({
|
|
|
556
653
|
exports.defaultTokenFilePath = defaultTokenFilePath2;
|
|
557
654
|
exports.persistEventWriteToken = persistEventWriteToken2;
|
|
558
655
|
exports.readTokenFile = readTokenFile2;
|
|
656
|
+
exports.sanitizeFilePart = sanitizeFilePart;
|
|
559
657
|
var fs = __importStar(__require("fs"));
|
|
560
658
|
var os = __importStar(__require("os"));
|
|
561
659
|
var path = __importStar(__require("path"));
|
|
@@ -587,15 +685,144 @@ var require_tokenStore = __commonJS({
|
|
|
587
685
|
}
|
|
588
686
|
});
|
|
589
687
|
|
|
688
|
+
// ../packages/tool-kit/out/stateStore.js
|
|
689
|
+
var require_stateStore = __commonJS({
|
|
690
|
+
"../packages/tool-kit/out/stateStore.js"(exports) {
|
|
691
|
+
"use strict";
|
|
692
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
693
|
+
if (k2 === void 0) k2 = k;
|
|
694
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
695
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
696
|
+
desc = { enumerable: true, get: function() {
|
|
697
|
+
return m[k];
|
|
698
|
+
} };
|
|
699
|
+
}
|
|
700
|
+
Object.defineProperty(o, k2, desc);
|
|
701
|
+
} : function(o, m, k, k2) {
|
|
702
|
+
if (k2 === void 0) k2 = k;
|
|
703
|
+
o[k2] = m[k];
|
|
704
|
+
});
|
|
705
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
706
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
707
|
+
} : function(o, v) {
|
|
708
|
+
o["default"] = v;
|
|
709
|
+
});
|
|
710
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
711
|
+
var ownKeys = function(o) {
|
|
712
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
713
|
+
var ar = [];
|
|
714
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
715
|
+
return ar;
|
|
716
|
+
};
|
|
717
|
+
return ownKeys(o);
|
|
718
|
+
};
|
|
719
|
+
return function(mod) {
|
|
720
|
+
if (mod && mod.__esModule) return mod;
|
|
721
|
+
var result = {};
|
|
722
|
+
if (mod != null) {
|
|
723
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
724
|
+
}
|
|
725
|
+
__setModuleDefault(result, mod);
|
|
726
|
+
return result;
|
|
727
|
+
};
|
|
728
|
+
}();
|
|
729
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
730
|
+
exports.defaultStateFilePath = defaultStateFilePath;
|
|
731
|
+
exports.readCollectorState = readCollectorState;
|
|
732
|
+
exports.recordSendOutcome = recordSendOutcome;
|
|
733
|
+
exports.shouldAnnounceFailure = shouldAnnounceFailure;
|
|
734
|
+
exports.markFailureNotified = markFailureNotified;
|
|
735
|
+
var fs = __importStar(__require("fs"));
|
|
736
|
+
var os = __importStar(__require("os"));
|
|
737
|
+
var path = __importStar(__require("path"));
|
|
738
|
+
var tokenStore_1 = require_tokenStore();
|
|
739
|
+
function defaultStateFilePath(toolInstallationId) {
|
|
740
|
+
return path.join(os.homedir(), ".ascenda", "state", `${(0, tokenStore_1.sanitizeFilePart)(toolInstallationId)}.json`);
|
|
741
|
+
}
|
|
742
|
+
function readCollectorState(stateFilePath) {
|
|
743
|
+
try {
|
|
744
|
+
if (!fs.existsSync(stateFilePath))
|
|
745
|
+
return void 0;
|
|
746
|
+
const raw = fs.readFileSync(stateFilePath, "utf8").trim();
|
|
747
|
+
if (!raw)
|
|
748
|
+
return void 0;
|
|
749
|
+
const parsed = JSON.parse(raw);
|
|
750
|
+
if (!parsed || typeof parsed !== "object")
|
|
751
|
+
return void 0;
|
|
752
|
+
return parsed;
|
|
753
|
+
} catch {
|
|
754
|
+
return void 0;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
function recordSendOutcome(stateFilePath, toolInstallationId, outcome, detail = {}) {
|
|
758
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
759
|
+
const previous = readCollectorState(stateFilePath);
|
|
760
|
+
const accepted = outcome === "accepted";
|
|
761
|
+
const next = {
|
|
762
|
+
toolInstallationId,
|
|
763
|
+
lastAttemptAt: now,
|
|
764
|
+
lastSuccessAt: accepted ? now : previous?.lastSuccessAt,
|
|
765
|
+
lastOutcome: outcome,
|
|
766
|
+
consecutiveFailures: accepted ? 0 : (previous?.consecutiveFailures ?? 0) + 1,
|
|
767
|
+
...detail.httpStatus !== void 0 ? { httpStatus: detail.httpStatus } : {},
|
|
768
|
+
...detail.errorCode !== void 0 ? { errorCode: detail.errorCode } : {},
|
|
769
|
+
...detail.detail !== void 0 ? { detail: truncate(detail.detail) } : {},
|
|
770
|
+
// A success closes the episode; a failure either opens one or continues
|
|
771
|
+
// the one already open. Carrying `notifiedFailingSince` across a
|
|
772
|
+
// continuing episode is what keeps the notice to once per outage.
|
|
773
|
+
...accepted ? {} : { failingSince: previous?.failingSince ?? now },
|
|
774
|
+
...accepted ? {} : previous?.notifiedFailingSince !== void 0 ? { notifiedFailingSince: previous.notifiedFailingSince } : {}
|
|
775
|
+
};
|
|
776
|
+
writeStateFile(stateFilePath, next);
|
|
777
|
+
return next;
|
|
778
|
+
}
|
|
779
|
+
function shouldAnnounceFailure(state) {
|
|
780
|
+
if (!state || state.consecutiveFailures === 0 || !state.failingSince)
|
|
781
|
+
return false;
|
|
782
|
+
return state.notifiedFailingSince !== state.failingSince;
|
|
783
|
+
}
|
|
784
|
+
function markFailureNotified(stateFilePath, state) {
|
|
785
|
+
if (!state.failingSince)
|
|
786
|
+
return;
|
|
787
|
+
writeStateFile(stateFilePath, { ...state, notifiedFailingSince: state.failingSince });
|
|
788
|
+
}
|
|
789
|
+
function writeStateFile(stateFilePath, state) {
|
|
790
|
+
const temporaryPath = `${stateFilePath}.${process.pid}.tmp`;
|
|
791
|
+
try {
|
|
792
|
+
const dir = path.dirname(stateFilePath);
|
|
793
|
+
fs.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
794
|
+
fs.writeFileSync(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
795
|
+
`, { encoding: "utf8", mode: 384 });
|
|
796
|
+
fs.renameSync(temporaryPath, stateFilePath);
|
|
797
|
+
if (process.platform !== "win32")
|
|
798
|
+
fs.chmodSync(stateFilePath, 384);
|
|
799
|
+
} catch {
|
|
800
|
+
try {
|
|
801
|
+
if (fs.existsSync(temporaryPath))
|
|
802
|
+
fs.unlinkSync(temporaryPath);
|
|
803
|
+
} catch {
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
function truncate(value) {
|
|
808
|
+
const collapsed = value.replace(/\s+/g, " ").trim();
|
|
809
|
+
return collapsed.length > 200 ? `${collapsed.slice(0, 197)}...` : collapsed;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
590
814
|
// ../packages/tool-kit/out/eventSender.js
|
|
591
815
|
var require_eventSender = __commonJS({
|
|
592
816
|
"../packages/tool-kit/out/eventSender.js"(exports) {
|
|
593
817
|
"use strict";
|
|
594
818
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
595
819
|
exports.AscendaEventSender = exports.AscendaSemanticEventError = void 0;
|
|
820
|
+
exports.buildEventPayload = buildEventPayload;
|
|
596
821
|
var tool_contract_1 = require_out();
|
|
822
|
+
var eventLog_1 = require_eventLog();
|
|
597
823
|
var http_1 = require_http();
|
|
598
824
|
var tokenStore_1 = require_tokenStore();
|
|
825
|
+
var stateStore_1 = require_stateStore();
|
|
599
826
|
var AscendaSemanticEventError = class extends Error {
|
|
600
827
|
constructor(message) {
|
|
601
828
|
super(message);
|
|
@@ -603,27 +830,32 @@ var require_eventSender = __commonJS({
|
|
|
603
830
|
}
|
|
604
831
|
};
|
|
605
832
|
exports.AscendaSemanticEventError = AscendaSemanticEventError;
|
|
833
|
+
function buildEventPayload(identity, mapped) {
|
|
834
|
+
return {
|
|
835
|
+
toolInstallationId: identity.toolInstallationId,
|
|
836
|
+
source: identity.source,
|
|
837
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
838
|
+
sessionId: identity.sessionId ?? void 0,
|
|
839
|
+
workspaceHash: identity.workspaceHash ?? void 0,
|
|
840
|
+
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
841
|
+
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
842
|
+
privacyMode: "metadata_only",
|
|
843
|
+
...mapped,
|
|
844
|
+
metadata: mapped.metadata ?? {}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
848
|
+
var RETRY_DELAY_MS = 250;
|
|
606
849
|
var AscendaEventSender2 = class {
|
|
607
850
|
config;
|
|
608
851
|
eventWriteToken;
|
|
852
|
+
lastState;
|
|
609
853
|
constructor(config2) {
|
|
610
854
|
this.config = config2;
|
|
611
855
|
this.eventWriteToken = config2.eventWriteToken;
|
|
612
856
|
}
|
|
613
857
|
async send(mapped) {
|
|
614
|
-
|
|
615
|
-
toolInstallationId: this.config.toolInstallationId,
|
|
616
|
-
source: this.config.source,
|
|
617
|
-
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
618
|
-
sessionId: this.config.sessionId ?? void 0,
|
|
619
|
-
workspaceHash: this.config.workspaceHash ?? void 0,
|
|
620
|
-
consentScope: tool_contract_1.ASCENDA_CONSENT_SCOPE,
|
|
621
|
-
provenance: tool_contract_1.ASCENDA_PROVENANCE,
|
|
622
|
-
privacyMode: "metadata_only",
|
|
623
|
-
...mapped,
|
|
624
|
-
metadata: mapped.metadata ?? {}
|
|
625
|
-
};
|
|
626
|
-
return this.post(payload);
|
|
858
|
+
return this.post(buildEventPayload(this.config, mapped));
|
|
627
859
|
}
|
|
628
860
|
/**
|
|
629
861
|
* Sends one of the six agent-observed types (dark-flow-gap-analysis §2.1).
|
|
@@ -694,32 +926,263 @@ var require_eventSender = __commonJS({
|
|
|
694
926
|
};
|
|
695
927
|
return this.post(payload);
|
|
696
928
|
}
|
|
929
|
+
/**
|
|
930
|
+
* The single choke point every event passes through, and therefore the only
|
|
931
|
+
* honest place to journal one. Recording here rather than in each adapter is
|
|
932
|
+
* deliberate: Claude Code, Codex, the GitHub collector and the MCP server all
|
|
933
|
+
* send through this method, and the defect being fixed showed up in three
|
|
934
|
+
* separate components because each was left to notice its own failures.
|
|
935
|
+
*/
|
|
697
936
|
async post(payload) {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
937
|
+
const outcome = await this.attempt(payload);
|
|
938
|
+
this.lastState = (0, stateStore_1.recordSendOutcome)(this.stateFilePath(), this.config.toolInstallationId, outcome.result, {
|
|
939
|
+
httpStatus: outcome.httpStatus,
|
|
940
|
+
errorCode: outcome.errorCode,
|
|
941
|
+
detail: outcome.detail
|
|
942
|
+
});
|
|
943
|
+
this.log(payload, outcome.result);
|
|
944
|
+
return outcome.result;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Two recoveries, each tried once. A rejected token gets a renewal and one
|
|
948
|
+
* replay — rotation is expected and should heal unattended. A transport
|
|
949
|
+
* error gets one retry, because the common cases (a restarting instance, a
|
|
950
|
+
* proxy blip, a 429) clear in well under a second and the alternative is
|
|
951
|
+
* losing the event outright.
|
|
952
|
+
*/
|
|
953
|
+
async attempt(payload) {
|
|
954
|
+
const outcome = await (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
955
|
+
if (outcome.result === "auth_failed") {
|
|
956
|
+
if (!await this.renewEventToken())
|
|
957
|
+
return outcome;
|
|
958
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
704
959
|
}
|
|
705
|
-
|
|
960
|
+
if (outcome.result === "transport_error" && (outcome.httpStatus === void 0 || (0, http_1.isRetryableStatus)(outcome.httpStatus))) {
|
|
961
|
+
await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY_MS));
|
|
962
|
+
return (0, http_1.postToolEvent)(this.config.apiBaseUrl, this.eventWriteToken, payload, this.signal());
|
|
963
|
+
}
|
|
964
|
+
return outcome;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* The state written by the most recent send, so a caller can decide whether
|
|
968
|
+
* to surface a one-time notice without re-reading the journal it just wrote.
|
|
969
|
+
*/
|
|
970
|
+
get state() {
|
|
971
|
+
return this.lastState;
|
|
972
|
+
}
|
|
973
|
+
stateFilePath() {
|
|
974
|
+
return this.config.stateFilePath ?? (0, stateStore_1.defaultStateFilePath)(this.config.toolInstallationId);
|
|
975
|
+
}
|
|
976
|
+
/** Never throws: a renewal that errors is a failed renewal, not a failed turn. */
|
|
977
|
+
/**
|
|
978
|
+
* Every send path funnels through {@link post}, so semantic and
|
|
979
|
+
* collaboration signals are logged on the same terms as host events — the
|
|
980
|
+
* log would be misleading as an audit of what left the machine otherwise.
|
|
981
|
+
*
|
|
982
|
+
* An unreachable backend used to be logged as `other` from a catch block.
|
|
983
|
+
* It is now `transport_error` through the ordinary path, because the
|
|
984
|
+
* transport returns that outcome instead of throwing.
|
|
985
|
+
*/
|
|
986
|
+
log(payload, delivery) {
|
|
987
|
+
const logFile = this.config.eventLogFile === void 0 ? (0, eventLog_1.resolveEventLogPath)() : this.config.eventLogFile;
|
|
988
|
+
if (!logFile)
|
|
989
|
+
return;
|
|
990
|
+
(0, eventLog_1.appendEventLog)(logFile, { loggedAt: (/* @__PURE__ */ new Date()).toISOString(), delivery, payload });
|
|
706
991
|
}
|
|
707
992
|
async renewEventToken() {
|
|
708
|
-
|
|
709
|
-
|
|
993
|
+
try {
|
|
994
|
+
const renewed = await (0, http_1.renewToolToken)(this.config.apiBaseUrl, this.eventWriteToken, this.signal());
|
|
995
|
+
if (!renewed)
|
|
996
|
+
return false;
|
|
997
|
+
this.eventWriteToken = renewed.eventWriteToken;
|
|
998
|
+
(0, tokenStore_1.persistEventWriteToken)(this.config.tokenFilePath, renewed.eventWriteToken);
|
|
999
|
+
return true;
|
|
1000
|
+
} catch {
|
|
710
1001
|
return false;
|
|
711
|
-
|
|
712
|
-
(0, tokenStore_1.persistEventWriteToken)(this.config.tokenFilePath, renewed.eventWriteToken);
|
|
713
|
-
return true;
|
|
1002
|
+
}
|
|
714
1003
|
}
|
|
715
1004
|
signal() {
|
|
716
|
-
return
|
|
1005
|
+
return AbortSignal.timeout(this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
717
1006
|
}
|
|
718
1007
|
};
|
|
719
1008
|
exports.AscendaEventSender = AscendaEventSender2;
|
|
720
1009
|
}
|
|
721
1010
|
});
|
|
722
1011
|
|
|
1012
|
+
// ../packages/tool-kit/out/hookAdapter.js
|
|
1013
|
+
var require_hookAdapter = __commonJS({
|
|
1014
|
+
"../packages/tool-kit/out/hookAdapter.js"(exports) {
|
|
1015
|
+
"use strict";
|
|
1016
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1017
|
+
exports.DEFAULT_API_BASE_URL = void 0;
|
|
1018
|
+
exports.loadCliAgentConfig = loadCliAgentConfig;
|
|
1019
|
+
exports.deliverHookEvents = deliverHookEvents;
|
|
1020
|
+
var eventLog_1 = require_eventLog();
|
|
1021
|
+
var eventSender_1 = require_eventSender();
|
|
1022
|
+
var tokenStore_1 = require_tokenStore();
|
|
1023
|
+
exports.DEFAULT_API_BASE_URL = "https://api.ascenda.one";
|
|
1024
|
+
function loadCliAgentConfig(toolType, sessionIdFromHook) {
|
|
1025
|
+
const apiBaseUrl = (process.env.ASCENDA_API_BASE_URL ?? exports.DEFAULT_API_BASE_URL).replace(/\/$/, "");
|
|
1026
|
+
const toolInstallationIdRaw = process.env.ASCENDA_TOOL_INSTALLATION_ID;
|
|
1027
|
+
if (!toolInstallationIdRaw)
|
|
1028
|
+
throw new Error("Missing ASCENDA_TOOL_INSTALLATION_ID");
|
|
1029
|
+
const toolInstallationId = toolInstallationIdRaw.trim().includes(":") ? toolInstallationIdRaw.trim() : `${toolType}:${toolInstallationIdRaw.trim()}`;
|
|
1030
|
+
const tokenFilePath = process.env.ASCENDA_EVENT_WRITE_TOKEN_FILE ?? (0, tokenStore_1.defaultTokenFilePath)(toolInstallationId);
|
|
1031
|
+
const fileToken = (0, tokenStore_1.readTokenFile)(tokenFilePath);
|
|
1032
|
+
const eventWriteToken = fileToken ?? process.env.ASCENDA_EVENT_WRITE_TOKEN;
|
|
1033
|
+
if (!eventWriteToken)
|
|
1034
|
+
throw new Error("Missing ASCENDA_EVENT_WRITE_TOKEN (or token file)");
|
|
1035
|
+
if (!fileToken)
|
|
1036
|
+
(0, tokenStore_1.persistEventWriteToken)(tokenFilePath, eventWriteToken);
|
|
1037
|
+
return {
|
|
1038
|
+
apiBaseUrl,
|
|
1039
|
+
toolInstallationId,
|
|
1040
|
+
eventWriteToken,
|
|
1041
|
+
tokenFilePath,
|
|
1042
|
+
sessionId: process.env.ASCENDA_SESSION_ID ?? sessionIdFromHook ?? null,
|
|
1043
|
+
workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null,
|
|
1044
|
+
// Agents await command hooks; fail fast rather than stall the user's turn.
|
|
1045
|
+
timeoutMs: parsePositiveInt(process.env.ASCENDA_HTTP_TIMEOUT_MS) ?? 3e3
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
async function deliverHookEvents(events, options) {
|
|
1049
|
+
if (events.length === 0)
|
|
1050
|
+
return;
|
|
1051
|
+
const notice = options.onNotice ?? ((message) => console.error(message));
|
|
1052
|
+
let config2;
|
|
1053
|
+
try {
|
|
1054
|
+
config2 = loadCliAgentConfig(options.toolType, options.sessionId);
|
|
1055
|
+
} catch (error2) {
|
|
1056
|
+
const logFile = (0, eventLog_1.resolveEventLogPath)();
|
|
1057
|
+
if (!logFile)
|
|
1058
|
+
throw error2;
|
|
1059
|
+
for (const event of events) {
|
|
1060
|
+
(0, eventLog_1.appendEventLog)(logFile, {
|
|
1061
|
+
loggedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1062
|
+
delivery: "not_sent",
|
|
1063
|
+
payload: (0, eventSender_1.buildEventPayload)({
|
|
1064
|
+
toolInstallationId: `${options.toolType}:unpaired`,
|
|
1065
|
+
source: options.source,
|
|
1066
|
+
sessionId: options.sessionId ?? null,
|
|
1067
|
+
workspaceHash: process.env.ASCENDA_WORKSPACE_HASH ?? null
|
|
1068
|
+
}, event)
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1073
|
+
const sender = new eventSender_1.AscendaEventSender({
|
|
1074
|
+
apiBaseUrl: config2.apiBaseUrl,
|
|
1075
|
+
toolInstallationId: config2.toolInstallationId,
|
|
1076
|
+
source: options.source,
|
|
1077
|
+
eventWriteToken: config2.eventWriteToken,
|
|
1078
|
+
tokenFilePath: config2.tokenFilePath,
|
|
1079
|
+
sessionId: config2.sessionId,
|
|
1080
|
+
workspaceHash: config2.workspaceHash,
|
|
1081
|
+
timeoutMs: config2.timeoutMs
|
|
1082
|
+
});
|
|
1083
|
+
for (const event of events) {
|
|
1084
|
+
const result = await sender.send(event);
|
|
1085
|
+
if (result === "accepted")
|
|
1086
|
+
continue;
|
|
1087
|
+
if (result === "consent_missing") {
|
|
1088
|
+
notice("Ascenda telemetry paused: renew IDE telemetry consent in the Ascenda app.");
|
|
1089
|
+
} else if (result === "auth_failed") {
|
|
1090
|
+
notice("Ascenda telemetry paused: connection revoked or expired. Re-pair via an Ascenda IDE extension or pairing-sim.");
|
|
1091
|
+
} else if (result === "transport_error") {
|
|
1092
|
+
notice("Ascenda telemetry paused: the ingest endpoint could not be reached. Your work is unaffected.");
|
|
1093
|
+
} else {
|
|
1094
|
+
notice(`Ascenda telemetry rejected: ${result}`);
|
|
1095
|
+
}
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
function parsePositiveInt(value) {
|
|
1100
|
+
const n = Number(value);
|
|
1101
|
+
return Number.isInteger(n) && n > 0 ? n : void 0;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
|
|
1106
|
+
// ../packages/tool-kit/out/turnState.js
|
|
1107
|
+
var require_turnState = __commonJS({
|
|
1108
|
+
"../packages/tool-kit/out/turnState.js"(exports) {
|
|
1109
|
+
"use strict";
|
|
1110
|
+
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
|
|
1111
|
+
if (k2 === void 0) k2 = k;
|
|
1112
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
1113
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
1114
|
+
desc = { enumerable: true, get: function() {
|
|
1115
|
+
return m[k];
|
|
1116
|
+
} };
|
|
1117
|
+
}
|
|
1118
|
+
Object.defineProperty(o, k2, desc);
|
|
1119
|
+
} : function(o, m, k, k2) {
|
|
1120
|
+
if (k2 === void 0) k2 = k;
|
|
1121
|
+
o[k2] = m[k];
|
|
1122
|
+
});
|
|
1123
|
+
var __setModuleDefault = exports && exports.__setModuleDefault || (Object.create ? function(o, v) {
|
|
1124
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
1125
|
+
} : function(o, v) {
|
|
1126
|
+
o["default"] = v;
|
|
1127
|
+
});
|
|
1128
|
+
var __importStar = exports && exports.__importStar || /* @__PURE__ */ function() {
|
|
1129
|
+
var ownKeys = function(o) {
|
|
1130
|
+
ownKeys = Object.getOwnPropertyNames || function(o2) {
|
|
1131
|
+
var ar = [];
|
|
1132
|
+
for (var k in o2) if (Object.prototype.hasOwnProperty.call(o2, k)) ar[ar.length] = k;
|
|
1133
|
+
return ar;
|
|
1134
|
+
};
|
|
1135
|
+
return ownKeys(o);
|
|
1136
|
+
};
|
|
1137
|
+
return function(mod) {
|
|
1138
|
+
if (mod && mod.__esModule) return mod;
|
|
1139
|
+
var result = {};
|
|
1140
|
+
if (mod != null) {
|
|
1141
|
+
for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
1142
|
+
}
|
|
1143
|
+
__setModuleDefault(result, mod);
|
|
1144
|
+
return result;
|
|
1145
|
+
};
|
|
1146
|
+
}();
|
|
1147
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1148
|
+
exports.recordTurnStart = recordTurnStart;
|
|
1149
|
+
exports.consumeTurnDurationMs = consumeTurnDurationMs;
|
|
1150
|
+
var fs = __importStar(__require("fs"));
|
|
1151
|
+
var os = __importStar(__require("os"));
|
|
1152
|
+
var path = __importStar(__require("path"));
|
|
1153
|
+
var stateDir = () => process.env.ASCENDA_STATE_DIR ?? path.join(os.homedir(), ".ascenda", "state");
|
|
1154
|
+
function turnFile(agent, sessionId) {
|
|
1155
|
+
return path.join(stateDir(), `${sanitize(agent)}-turn-${sanitize(sessionId)}`);
|
|
1156
|
+
}
|
|
1157
|
+
function sanitize(value) {
|
|
1158
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
1159
|
+
}
|
|
1160
|
+
function recordTurnStart(agent, sessionId, now = Date.now()) {
|
|
1161
|
+
if (!sessionId)
|
|
1162
|
+
return;
|
|
1163
|
+
try {
|
|
1164
|
+
fs.mkdirSync(stateDir(), { recursive: true });
|
|
1165
|
+
fs.writeFileSync(turnFile(agent, sessionId), String(now), { encoding: "utf8", mode: 384 });
|
|
1166
|
+
} catch {
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
function consumeTurnDurationMs(agent, sessionId, now = Date.now()) {
|
|
1170
|
+
if (!sessionId)
|
|
1171
|
+
return void 0;
|
|
1172
|
+
const file = turnFile(agent, sessionId);
|
|
1173
|
+
try {
|
|
1174
|
+
const started = Number(fs.readFileSync(file, "utf8").trim());
|
|
1175
|
+
fs.rmSync(file, { force: true });
|
|
1176
|
+
if (!Number.isFinite(started) || started <= 0 || started > now)
|
|
1177
|
+
return void 0;
|
|
1178
|
+
return now - started;
|
|
1179
|
+
} catch {
|
|
1180
|
+
return void 0;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
|
|
723
1186
|
// ../packages/tool-kit/out/salt.js
|
|
724
1187
|
var require_salt = __commonJS({
|
|
725
1188
|
"../packages/tool-kit/out/salt.js"(exports) {
|
|
@@ -934,7 +1397,8 @@ var require_out2 = __commonJS({
|
|
|
934
1397
|
"../packages/tool-kit/out/index.js"(exports) {
|
|
935
1398
|
"use strict";
|
|
936
1399
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
937
|
-
exports.
|
|
1400
|
+
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;
|
|
1401
|
+
exports.isRetryableStatus = exports.parseIngestResponse = void 0;
|
|
938
1402
|
var commandClassifier_1 = require_commandClassifier();
|
|
939
1403
|
Object.defineProperty(exports, "classifyCommand", { enumerable: true, get: function() {
|
|
940
1404
|
return commandClassifier_1.classifyCommand;
|
|
@@ -999,6 +1463,39 @@ var require_out2 = __commonJS({
|
|
|
999
1463
|
Object.defineProperty(exports, "AscendaSemanticEventError", { enumerable: true, get: function() {
|
|
1000
1464
|
return eventSender_1.AscendaSemanticEventError;
|
|
1001
1465
|
} });
|
|
1466
|
+
Object.defineProperty(exports, "buildEventPayload", { enumerable: true, get: function() {
|
|
1467
|
+
return eventSender_1.buildEventPayload;
|
|
1468
|
+
} });
|
|
1469
|
+
var eventLog_1 = require_eventLog();
|
|
1470
|
+
Object.defineProperty(exports, "EVENT_LOG_ENV_VAR", { enumerable: true, get: function() {
|
|
1471
|
+
return eventLog_1.EVENT_LOG_ENV_VAR;
|
|
1472
|
+
} });
|
|
1473
|
+
Object.defineProperty(exports, "appendEventLog", { enumerable: true, get: function() {
|
|
1474
|
+
return eventLog_1.appendEventLog;
|
|
1475
|
+
} });
|
|
1476
|
+
Object.defineProperty(exports, "expandUserPath", { enumerable: true, get: function() {
|
|
1477
|
+
return eventLog_1.expandUserPath;
|
|
1478
|
+
} });
|
|
1479
|
+
Object.defineProperty(exports, "resolveEventLogPath", { enumerable: true, get: function() {
|
|
1480
|
+
return eventLog_1.resolveEventLogPath;
|
|
1481
|
+
} });
|
|
1482
|
+
var hookAdapter_1 = require_hookAdapter();
|
|
1483
|
+
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", { enumerable: true, get: function() {
|
|
1484
|
+
return hookAdapter_1.DEFAULT_API_BASE_URL;
|
|
1485
|
+
} });
|
|
1486
|
+
Object.defineProperty(exports, "deliverHookEvents", { enumerable: true, get: function() {
|
|
1487
|
+
return hookAdapter_1.deliverHookEvents;
|
|
1488
|
+
} });
|
|
1489
|
+
Object.defineProperty(exports, "loadCliAgentConfig", { enumerable: true, get: function() {
|
|
1490
|
+
return hookAdapter_1.loadCliAgentConfig;
|
|
1491
|
+
} });
|
|
1492
|
+
var turnState_1 = require_turnState();
|
|
1493
|
+
Object.defineProperty(exports, "consumeTurnDurationMs", { enumerable: true, get: function() {
|
|
1494
|
+
return turnState_1.consumeTurnDurationMs;
|
|
1495
|
+
} });
|
|
1496
|
+
Object.defineProperty(exports, "recordTurnStart", { enumerable: true, get: function() {
|
|
1497
|
+
return turnState_1.recordTurnStart;
|
|
1498
|
+
} });
|
|
1002
1499
|
var tokenStore_1 = require_tokenStore();
|
|
1003
1500
|
Object.defineProperty(exports, "defaultTokenFilePath", { enumerable: true, get: function() {
|
|
1004
1501
|
return tokenStore_1.defaultTokenFilePath;
|
|
@@ -1009,6 +1506,22 @@ var require_out2 = __commonJS({
|
|
|
1009
1506
|
Object.defineProperty(exports, "readTokenFile", { enumerable: true, get: function() {
|
|
1010
1507
|
return tokenStore_1.readTokenFile;
|
|
1011
1508
|
} });
|
|
1509
|
+
var stateStore_1 = require_stateStore();
|
|
1510
|
+
Object.defineProperty(exports, "defaultStateFilePath", { enumerable: true, get: function() {
|
|
1511
|
+
return stateStore_1.defaultStateFilePath;
|
|
1512
|
+
} });
|
|
1513
|
+
Object.defineProperty(exports, "readCollectorState", { enumerable: true, get: function() {
|
|
1514
|
+
return stateStore_1.readCollectorState;
|
|
1515
|
+
} });
|
|
1516
|
+
Object.defineProperty(exports, "recordSendOutcome", { enumerable: true, get: function() {
|
|
1517
|
+
return stateStore_1.recordSendOutcome;
|
|
1518
|
+
} });
|
|
1519
|
+
Object.defineProperty(exports, "shouldAnnounceFailure", { enumerable: true, get: function() {
|
|
1520
|
+
return stateStore_1.shouldAnnounceFailure;
|
|
1521
|
+
} });
|
|
1522
|
+
Object.defineProperty(exports, "markFailureNotified", { enumerable: true, get: function() {
|
|
1523
|
+
return stateStore_1.markFailureNotified;
|
|
1524
|
+
} });
|
|
1012
1525
|
var salt_1 = require_salt();
|
|
1013
1526
|
Object.defineProperty(exports, "machineSaltFilePath", { enumerable: true, get: function() {
|
|
1014
1527
|
return salt_1.machineSaltFilePath;
|
|
@@ -1054,6 +1567,9 @@ var require_out2 = __commonJS({
|
|
|
1054
1567
|
Object.defineProperty(exports, "parseIngestResponse", { enumerable: true, get: function() {
|
|
1055
1568
|
return http_1.parseIngestResponse;
|
|
1056
1569
|
} });
|
|
1570
|
+
Object.defineProperty(exports, "isRetryableStatus", { enumerable: true, get: function() {
|
|
1571
|
+
return http_1.isRetryableStatus;
|
|
1572
|
+
} });
|
|
1057
1573
|
}
|
|
1058
1574
|
});
|
|
1059
1575
|
|
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.
|
|
4
|
+
"version": "0.1.10",
|
|
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",
|