@productbrain/mcp 0.0.1-beta.3503 → 0.0.1-beta.3516

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.
@@ -505,6 +505,515 @@ async function writeClientConfig(client2, apiKey) {
505
505
  return true;
506
506
  }
507
507
 
508
+ // src/generated/routeLatencyBudget.generated.ts
509
+ var ROUTE_LATENCY_BUDGET_MS = {
510
+ query: 1e4,
511
+ mutation: 1e4,
512
+ action: 3e4
513
+ };
514
+ var ROUTE_LATENCY_BUDGET_OVERRIDE_MS = {
515
+ // convex/intelligence/onboardingChat.ts:333 declares `timeoutMs: 45_000` for the extraction
516
+ // LLM call — 15s BEYOND the flat action budget. 60s clears it with room for the surrounding
517
+ // request/response work the 45s covers none of.
518
+ "onboarding.chat": 6e4,
519
+ // convex/intelligence/spineCheck.ts:210 sets SPINE_CHECK_WALL_CLOCK_BUDGET_MS = 4 * 60_000
520
+ // for its SEQUENTIAL strategy probes (:225-239, deliberately not Promise.all), and :215
521
+ // reserves a tail of one 25s probe + an optional 25s story pass + 10s. The server's own
522
+ // design ceiling is scheduleSpineCheck's 5-min MAX_RUN_IN_FLIGHT_MS lease (:206-209).
523
+ // 5.5 min sits above that ceiling and far under Convex's 10-min action cap.
524
+ "quality.spineCheck": 33e4
525
+ };
526
+ var WIDEST_ROUTE_LATENCY_BUDGET_MS = Math.max(
527
+ ...Object.values(ROUTE_LATENCY_BUDGET_MS),
528
+ ...Object.values(ROUTE_LATENCY_BUDGET_OVERRIDE_MS)
529
+ );
530
+ var ROUTE_TYPE_ENTRIES = {
531
+ "resolveWorkspace": "query",
532
+ "feedback.submit": "mutation",
533
+ "feedback.listOwn": "query",
534
+ "feedback.list": "action",
535
+ "feedback.note": "action",
536
+ "feedback.group": "action",
537
+ "feedback.status": "action",
538
+ "organisation.status": "query",
539
+ "organisation.assignMembership": "mutation",
540
+ "organisation.removeMembership": "mutation",
541
+ "organisation.setOwnership": "mutation",
542
+ "organisation.clearOwnership": "mutation",
543
+ "organisation.activate": "mutation",
544
+ "organisation.upgrade": "mutation",
545
+ "organisation.upgradeRung2": "mutation",
546
+ "organisation.upgradeRung3": "mutation",
547
+ "organisation.upgradeRung4": "mutation",
548
+ "organisation.updateGovernanceMode": "mutation",
549
+ "organisation.hierarchy.show": "query",
550
+ "organisation.hierarchy.setParent": "mutation",
551
+ "organisation.hierarchy.clearParent": "mutation",
552
+ "chain.seed": "mutation",
553
+ "chain.listCollections": "query",
554
+ "chain.getCollection": "query",
555
+ "chain.getCollectionFields": "query",
556
+ "chain.auditCollections": "query",
557
+ "chain.exportDefinitions": "query",
558
+ "chain.createCollection": "mutation",
559
+ "chain.updateCollection": "mutation",
560
+ "chain.listEntries": "query",
561
+ "chain.getEntry": "query",
562
+ "chain.batchGetEntries": "query",
563
+ "chain.createEntry": "action",
564
+ "chain.updateEntry": "mutation",
565
+ "chain.restoreArchivedEntry": "mutation",
566
+ "chain.moveToCollection": "mutation",
567
+ "chain.shapeAdvisories": "query",
568
+ "chain.shapeAdvisorySummary": "query",
569
+ "chain.showShapeAdvisory": "query",
570
+ "chain.dispositionShapeAdvisory": "mutation",
571
+ "conflicts.list": "query",
572
+ "conflicts.resolve": "mutation",
573
+ "conflicts.summary": "query",
574
+ "conflicts.snooze": "mutation",
575
+ "conflicts.reverdict": "mutation",
576
+ "direction.list": "query",
577
+ "direction.refresh": "action",
578
+ "direction.defer": "mutation",
579
+ "chain.classifyCollection": "action",
580
+ "chain.classifyStrategyCategory": "query",
581
+ "chain.batchClassifyHeuristic": "query",
582
+ "chain.resolveCollection": "action",
583
+ "chain.searchEntries": "query",
584
+ "chain.searchByCanonicalName": "query",
585
+ "chain.commitEntry": "mutation",
586
+ "chain.verifyEntry": "mutation",
587
+ "chain.batchCommitConstellation": "mutation",
588
+ "chain.listEntryHistory": "query",
589
+ "chain.listEntryVersions": "query",
590
+ "chain.createEntryRelation": "mutation",
591
+ "chain.createEntryRelations": "mutation",
592
+ "chain.removeEntryRelation": "mutation",
593
+ "chain.normalizeEntryDataLLM": "action",
594
+ "chain.decomposeContent": "action",
595
+ "chain.normalizeEntryDataPreview": "action",
596
+ "chain.listEntryRelations": "query",
597
+ "chain.scoreLinkCandidates": "query",
598
+ "chain.evaluateCoherence": "query",
599
+ "chain.listAutoLinkSuggestions": "query",
600
+ "chain.acceptAutoLinkSuggestion": "mutation",
601
+ "chain.dismissAutoLinkSuggestion": "mutation",
602
+ "chain.quarantineAutoLinkSuggestion": "mutation",
603
+ "chain.resurrectAutoLinkSuggestion": "mutation",
604
+ "chain.expireAutoLinkSuggestion": "mutation",
605
+ "chain.clusterAutoLinkSuggestions": "query",
606
+ "chain.batchApplyAutoLinkSuggestions": "action",
607
+ "chain.validateCommitConstellation": "query",
608
+ "chain.getCaptureContract": "query",
609
+ "chain.gatherContext": "query",
610
+ "chain.getConstellation": "query",
611
+ "chain.auditBet": "query",
612
+ "agentKnowledge.facilitateEnvelope": "action",
613
+ "agentKnowledge.wrapupEnvelope": "action",
614
+ "agentKnowledge.captureEnvelope": "action",
615
+ "chain.graphSuggestLinks": "query",
616
+ "chain.graphGatherContext": "query",
617
+ "chain.assembleBuildContext": "query",
618
+ "chain.qualityCheck": "query",
619
+ "chain.changeDetection": "query",
620
+ "chain.structuralAggregation": "query",
621
+ "chain.detectSemanticConflicts": "action",
622
+ "chain.taskAwareGatherContext": "query",
623
+ "chain.journeyAwareGatherContext": "query",
624
+ "chain.resolveTaskStartup": "query",
625
+ "chain.getBindingGovernanceView": "query",
626
+ "chain.resolveTaskStartupHybrid": "action",
627
+ "chain.taskAwareHybridGatherContext": "action",
628
+ "chain.gatherFromSeeds": "query",
629
+ "chain.getEntryNeighborhood": "query",
630
+ "chain.deepChainWalk": "action",
631
+ "chain.recordBriefRun": "mutation",
632
+ "chain.getLastBriefRun": "query",
633
+ "chain.incrementalChanges": "query",
634
+ "chain.compoundQuery": "action",
635
+ "chain.dismissSuggestion": "mutation",
636
+ "chain.recordSessionSignal": "mutation",
637
+ "chain.workspaceReadiness": "query",
638
+ "chain.getCaptureHealth": "query",
639
+ "chain.getGroundingHealth": "query",
640
+ "chain.recordGroundingOutcome": "mutation",
641
+ "chain.suggestLinksForCapture": "query",
642
+ "chain.setOnboardingCompleted": "mutation",
643
+ "chain.checkCardinalityWarning": "query",
644
+ "chain.ingestDocument": "action",
645
+ "chain.getOrientEntries": "query",
646
+ "chain.getOrientView": "action",
647
+ "chain.getRitualsSurface": "query",
648
+ "chain.getGovernanceWithRelations": "action",
649
+ "chain.classifyGovernance": "query",
650
+ "chain.getVocabulary": "query",
651
+ "scoreboard.get": "action",
652
+ "rework.report": "mutation",
653
+ "chain.listLabels": "query",
654
+ "chain.createLabel": "mutation",
655
+ "chain.updateLabel": "mutation",
656
+ "chain.deleteLabel": "mutation",
657
+ "chain.applyLabel": "mutation",
658
+ "chain.removeLabel": "mutation",
659
+ "chain.listEntriesByLabel": "query",
660
+ "setup.getActiveSurface": "query",
661
+ "setup.materializeSetup": "action",
662
+ "setup.recordTamperRefusal": "mutation",
663
+ "setup.recordTransition": "mutation",
664
+ "setup.getCurrentSetupState": "query",
665
+ "setup.listAssetsForUser": "query",
666
+ "setup.ingestSetupAsset": "mutation",
667
+ "setup.ingestSetupAssetWithBody": "action",
668
+ "setup.fetchAssetBody": "action",
669
+ "setup.auditAssetBodies": "action",
670
+ "setup.repairAssetBody": "action",
671
+ "setup.listFailedAuditReceipts": "action",
672
+ "setup.markPersonalSetupAssetDormantFromSync": "mutation",
673
+ "setup.resolveSemanticRefs": "query",
674
+ "setup.updateLastProjectedHash": "mutation",
675
+ "setup.getSkillSystemHealth": "query",
676
+ "setup.recordActivationReceipt": "mutation",
677
+ "setup.recordSetupInvocation": "mutation",
678
+ "setup.getUserActivationState": "query",
679
+ "setup.getPbSetupState": "query",
680
+ "setup.getSkillBody": "action",
681
+ "setup.stampMcpOnlySurface": "mutation",
682
+ "setup.stampDetectedSurfaces": "mutation",
683
+ "setup.getKey32Snapshot": "query",
684
+ "setup.getKey33Snapshot": "query",
685
+ "gaps.record": "mutation",
686
+ "gaps.resolve": "mutation",
687
+ "gaps.top": "query",
688
+ "gaps.stats": "query",
689
+ "agent.startSession": "mutation",
690
+ "agent.resumeSession": "mutation",
691
+ "agent.closeSession": "mutation",
692
+ "agent.markOriented": "mutation",
693
+ "agent.touchSession": "mutation",
694
+ "agent.recordActivity": "mutation",
695
+ "agent.getSession": "query",
696
+ "agent.getActiveSession": "query",
697
+ "agent.recentSessions": "query",
698
+ "agent.activityStats": "query",
699
+ "agent.getActivityByDay": "query",
700
+ "agent.validateSession": "query",
701
+ "agent.getSessionWrapup": "query",
702
+ "agent.recordWrapup": "mutation",
703
+ "agent.reportOrientMetric": "mutation",
704
+ "agent.listSessions": "query",
705
+ "agent.showConversation": "query",
706
+ "usage.getWorkspaceSummary": "query",
707
+ "quality.evaluateHeuristicAndSchedule": "mutation",
708
+ "quality.reEvaluateEntry": "mutation",
709
+ "quality.evaluateAtCapture": "action",
710
+ "quality.evaluateAtCommit": "action",
711
+ "quality.evaluateForReview": "action",
712
+ "quality.getCachedVerdict": "query",
713
+ "quality.getLatestVerdictForEntry": "query",
714
+ "quality.spineCheck": "action",
715
+ "quality.scheduleSpineCheck": "mutation",
716
+ "quality.getLatestSpineVerdict": "query",
717
+ "onboarding.chat": "action",
718
+ "workspace.health": "query",
719
+ "workspace.healthAll": "query",
720
+ "workspace.backfill": "mutation",
721
+ "workspace.backfillAll": "mutation",
722
+ "authorityDomains.readiness": "query",
723
+ "authorityDomains.add": "mutation",
724
+ "authorityDomains.propose": "action",
725
+ "authorityDomains.review": "query",
726
+ "authorityDomains.queueKnownTag": "mutation",
727
+ "authorityDomains.ratify": "mutation",
728
+ "authorityDomains.reject": "mutation",
729
+ "authorityDomains.discardPending": "mutation",
730
+ "authorityDomains.recordSample": "mutation",
731
+ "authorityDomains.benchmark": "action",
732
+ "authorityDomains.activateCutover": "mutation",
733
+ "authorityDomains.principleDistribution": "query",
734
+ "performance.getVitalsSummary": "query",
735
+ "performance.getApiOverview": "query",
736
+ "performance.getRouteBreakdown": "query",
737
+ "performance.getSlowSamples": "query",
738
+ "performance.getSampleCount": "query",
739
+ "chainwork.listTypes": "query",
740
+ "chainwork.getChainType": "query",
741
+ "chainwork.scoreRun": "query",
742
+ "chainwork.getArtifact": "query",
743
+ "chainwork.getWorkflowRun": "query",
744
+ "chainwork.getLatestWorkflowRun": "query",
745
+ "chainwork.recordWorkflowCheckpoint": "mutation",
746
+ "chainwork.finalizeWorkflowRun": "action",
747
+ "chainwork.submitToKG": "action",
748
+ "chainwork.generate": "action",
749
+ "chainwork.getLastVerifiedBrief": "query",
750
+ "gitchain.createChain": "mutation",
751
+ "gitchain.editLink": "mutation",
752
+ "gitchain.updateChain": "mutation",
753
+ "gitchain.getChain": "query",
754
+ "gitchain.listChains": "query",
755
+ "gitchain.getHistory": "query",
756
+ "gitchain.listCommits": "query",
757
+ "gitchain.commitChain": "mutation",
758
+ "gitchain.diffVersions": "mutation",
759
+ "gitchain.runGate": "query",
760
+ "gitchain.createBranch": "mutation",
761
+ "gitchain.listBranches": "mutation",
762
+ "gitchain.checkConflicts": "mutation",
763
+ "gitchain.mergeBranch": "mutation",
764
+ "gitchain.addComment": "mutation",
765
+ "gitchain.resolveComment": "mutation",
766
+ "gitchain.listComments": "mutation",
767
+ "gitchain.revertChain": "mutation",
768
+ "staging.getCommittedSourceRefs": "query",
769
+ "staging.commitStagingEntryWithClassification": "action",
770
+ "governance.listProposals": "query",
771
+ "governance.countOpenProposals": "query",
772
+ "governance.respondToProposal": "mutation",
773
+ "maps.createMap": "mutation",
774
+ "maps.createAudienceMapSet": "mutation",
775
+ "maps.addToSlot": "mutation",
776
+ "maps.removeFromSlot": "mutation",
777
+ "maps.replaceInSlot": "mutation",
778
+ "maps.commitMap": "mutation",
779
+ "maps.getMap": "query",
780
+ "maps.listMaps": "query",
781
+ "maps.getJourneyMapWithEnrichment": "query",
782
+ "maps.listJourneyMaps": "query",
783
+ "maps.listMapCommits": "query"
784
+ };
785
+ var ROUTE_TYPE_BY_NAME = Object.assign(
786
+ /* @__PURE__ */ Object.create(null),
787
+ ROUTE_TYPE_ENTRIES
788
+ );
789
+ function latencyBudgetMsForRouteType(type) {
790
+ return ROUTE_LATENCY_BUDGET_MS[type];
791
+ }
792
+ function latencyBudgetMsForRoute(routeName) {
793
+ if (Object.hasOwn(ROUTE_LATENCY_BUDGET_OVERRIDE_MS, routeName)) {
794
+ return ROUTE_LATENCY_BUDGET_OVERRIDE_MS[routeName];
795
+ }
796
+ const type = ROUTE_TYPE_BY_NAME[routeName];
797
+ return type ? latencyBudgetMsForRouteType(type) : WIDEST_ROUTE_LATENCY_BUDGET_MS;
798
+ }
799
+
800
+ // src/lib/gatewaySeamStore.ts
801
+ var AUDIT_BUFFER_SIZE = 50;
802
+ var auditBufferByWorkspace = /* @__PURE__ */ new Map();
803
+ var nextAuditSeq = 0;
804
+ function getAuditLog(scopes) {
805
+ const merged = [];
806
+ for (const scope of new Set(scopes)) {
807
+ const bucket = auditBufferByWorkspace.get(scope);
808
+ if (bucket) merged.push(...bucket);
809
+ }
810
+ return merged.sort((a, b) => a.seq - b.seq);
811
+ }
812
+ function emptyByRoute() {
813
+ return /* @__PURE__ */ Object.create(null);
814
+ }
815
+ function emptySeamCounters() {
816
+ return { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0, byRoute: emptyByRoute() };
817
+ }
818
+ var MAX_SEAM_WORKSPACES = 200;
819
+ var seamCountersByWorkspace = /* @__PURE__ */ new Map();
820
+ function touchBoundedWorkspaceMap(map, workspace, create) {
821
+ const existing = map.get(workspace);
822
+ if (existing !== void 0) {
823
+ map.delete(workspace);
824
+ map.set(workspace, existing);
825
+ return existing;
826
+ }
827
+ if (map.size >= MAX_SEAM_WORKSPACES) {
828
+ const leastRecentlyRecorded = map.keys().next().value;
829
+ if (leastRecentlyRecorded !== void 0) map.delete(leastRecentlyRecorded);
830
+ }
831
+ const created = create();
832
+ map.set(workspace, created);
833
+ return created;
834
+ }
835
+ function getOrCreateWorkspaceCounters(workspace) {
836
+ return touchBoundedWorkspaceMap(seamCountersByWorkspace, workspace, emptySeamCounters);
837
+ }
838
+ function getOrCreateWorkspaceAuditBuffer(workspace) {
839
+ return touchBoundedWorkspaceMap(auditBufferByWorkspace, workspace, () => []);
840
+ }
841
+ function getGatewaySeamCounters(workspace) {
842
+ const counters = seamCountersByWorkspace.get(workspace);
843
+ if (!counters) return emptySeamCounters();
844
+ return {
845
+ calls: counters.calls,
846
+ errors: counters.errors,
847
+ timeouts: counters.timeouts,
848
+ totalDurationMs: counters.totalDurationMs,
849
+ maxDurationMs: counters.maxDurationMs,
850
+ // Copied onto a null prototype, not left as `Object.fromEntries`' plain object — the
851
+ // snapshot is indexed by route name downstream (`getMergedGatewaySeamCounters`) and would
852
+ // reintroduce the inherited-member collision `emptySeamCounters` documents.
853
+ byRoute: Object.assign(emptyByRoute(), Object.fromEntries(Object.entries(counters.byRoute).map(([k, v]) => [k, { ...v }])))
854
+ };
855
+ }
856
+ function getMergedGatewaySeamCounters(scopes) {
857
+ const merged = emptySeamCounters();
858
+ for (const scope of new Set(scopes)) {
859
+ const part = getGatewaySeamCounters(scope);
860
+ merged.calls += part.calls;
861
+ merged.errors += part.errors;
862
+ merged.timeouts += part.timeouts;
863
+ merged.totalDurationMs += part.totalDurationMs;
864
+ merged.maxDurationMs = Math.max(merged.maxDurationMs, part.maxDurationMs);
865
+ for (const [route, r] of Object.entries(part.byRoute)) {
866
+ const into = merged.byRoute[route] ??= { calls: 0, errors: 0, timeouts: 0, totalDurationMs: 0, maxDurationMs: 0 };
867
+ into.calls += r.calls;
868
+ into.errors += r.errors;
869
+ into.timeouts += r.timeouts;
870
+ into.totalDurationMs += r.totalDurationMs;
871
+ into.maxDurationMs = Math.max(into.maxDurationMs, r.maxDurationMs);
872
+ }
873
+ }
874
+ return merged;
875
+ }
876
+ function appendAuditEntry(entry) {
877
+ const stored = { ...entry, seq: nextAuditSeq++ };
878
+ const bucket = getOrCreateWorkspaceAuditBuffer(stored.workspace);
879
+ bucket.push(stored);
880
+ if (bucket.length > AUDIT_BUFFER_SIZE) bucket.shift();
881
+ }
882
+ function recordSeamCounters(workspace, fn, status, durationMs, timedOut) {
883
+ const seamCounters = getOrCreateWorkspaceCounters(workspace);
884
+ const route = seamCounters.byRoute[fn] ??= {
885
+ calls: 0,
886
+ errors: 0,
887
+ timeouts: 0,
888
+ totalDurationMs: 0,
889
+ maxDurationMs: 0
890
+ };
891
+ seamCounters.calls += 1;
892
+ seamCounters.totalDurationMs += durationMs;
893
+ if (durationMs > seamCounters.maxDurationMs) seamCounters.maxDurationMs = durationMs;
894
+ route.calls += 1;
895
+ route.totalDurationMs += durationMs;
896
+ if (durationMs > route.maxDurationMs) route.maxDurationMs = durationMs;
897
+ if (status === "error") {
898
+ seamCounters.errors += 1;
899
+ route.errors += 1;
900
+ if (timedOut) {
901
+ seamCounters.timeouts += 1;
902
+ route.timeouts += 1;
903
+ }
904
+ }
905
+ }
906
+ function formatGatewaySeamSummary(seam) {
907
+ const lines = [
908
+ "\n\n---\n\n# Gateway seam (process lifetime)\n",
909
+ `Calls: ${seam.calls} \u2014 errors: ${seam.errors}, of which timeouts: ${seam.timeouts}`
910
+ ];
911
+ if (seam.calls > 0) {
912
+ lines.push(`Mean: ${Math.round(seam.totalDurationMs / seam.calls)}ms \u2014 slowest: ${seam.maxDurationMs}ms`);
913
+ }
914
+ if (seam.timeouts > 0) {
915
+ lines.push(`\u26A0 ${seam.timeouts} call(s) hit their latency budget \u2014 those outcomes are unknown, not failed.`);
916
+ }
917
+ return lines.join("\n");
918
+ }
919
+
920
+ // src/gatewaySeam.ts
921
+ var KernelCallError = class extends Error {
922
+ status;
923
+ code;
924
+ /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */
925
+ missingRequiredFields;
926
+ /** WP-316 S1a: Structured commit validation — field-level data errors. */
927
+ fieldErrors;
928
+ /**
929
+ * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope
930
+ * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at
931
+ * HTTP 200; without preserving them here a refused/blocked envelope would collapse
932
+ * into a bare code+message and the caller could not surface the per-offender routes.
933
+ */
934
+ diagnostics;
935
+ constructor(message, status, code, missingRequiredFields, fieldErrors, diagnostics) {
936
+ super(message);
937
+ this.name = "KernelCallError";
938
+ this.status = status;
939
+ this.code = code;
940
+ this.missingRequiredFields = missingRequiredFields;
941
+ this.fieldErrors = fieldErrors;
942
+ this.diagnostics = diagnostics;
943
+ }
944
+ };
945
+ var GatewayTimeoutError = class extends Error {
946
+ /** Gateway route name that was aborted. */
947
+ fn;
948
+ /** The declared budget, in ms, that this route was given (see routeLatencyBudget). */
949
+ budgetMs;
950
+ /** Wall-clock ms actually spent before the abort. */
951
+ elapsedMs;
952
+ /**
953
+ * True when the aborted call could have landed a write server-side despite this client
954
+ * giving up. Derived by `mayHaveLandedOnTimeout` from the route contract's own function
955
+ * type (`mutation`/`action` mutate; `query` does not) AND the call's own arguments (a
956
+ * `preview: true` dry run writes nothing) — never guessed from the name.
957
+ */
958
+ mayHaveLanded;
959
+ constructor(fn, budgetMs, elapsedMs, mayHaveLanded) {
960
+ super(
961
+ `MCP call "${fn}" exceeded its ${budgetMs}ms latency budget (waited ${elapsedMs}ms).` + (mayHaveLanded ? " This route writes, so the server may have completed it \u2014 verify before retrying." : "")
962
+ );
963
+ this.name = "GatewayTimeoutError";
964
+ this.fn = fn;
965
+ this.budgetMs = budgetMs;
966
+ this.elapsedMs = elapsedMs;
967
+ this.mayHaveLanded = mayHaveLanded;
968
+ }
969
+ };
970
+ function routeMayMutate(fn) {
971
+ const type = ROUTE_TYPE_BY_NAME[fn];
972
+ return type === void 0 || type !== "query";
973
+ }
974
+ function isDryRunCall(args) {
975
+ return args?.preview === true;
976
+ }
977
+ function mayHaveLandedOnTimeout(fn, args) {
978
+ return routeMayMutate(fn) && !isDryRunCall(args);
979
+ }
980
+ function throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, phase, record) {
981
+ if (err?.name === "TimeoutError" || err?.name === "AbortError") {
982
+ const timeoutErr = new GatewayTimeoutError(fn, budgetMs, elapsedMs, mayHaveLandedOnTimeout(fn, args));
983
+ record(timeoutErr.message, true);
984
+ throw timeoutErr;
985
+ }
986
+ const detail = err?.message ?? String(err);
987
+ record(phase === "network" ? detail : `${phase}: ${detail}`, false);
988
+ throw new Error(`MCP call "${fn}" ${phase} error: ${detail}`);
989
+ }
990
+ function emptyBodyOrThrowClassified(err, fn, args, budgetMs, elapsedMs, statusOk, record) {
991
+ if (statusOk) throwClassifiedGatewayFailure(err, fn, args, budgetMs, elapsedMs, "response body", record);
992
+ return {};
993
+ }
994
+ function shouldLogAudit(status) {
995
+ return status === "error" || process.env.MCP_DEBUG === "1";
996
+ }
997
+ function recordGatewayCall(params) {
998
+ const { fn, status, durationMs, workspace, errorMsg, toolContext, budgetMs, timedOut } = params;
999
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
1000
+ const entry = { ts, fn, workspace, status, durationMs };
1001
+ if (errorMsg) entry.error = errorMsg;
1002
+ if (toolContext) entry.toolContext = toolContext;
1003
+ if (budgetMs !== void 0) entry.budgetMs = budgetMs;
1004
+ if (timedOut) entry.timedOut = true;
1005
+ appendAuditEntry(entry);
1006
+ recordSeamCounters(workspace, fn, status, durationMs, timedOut === true);
1007
+ trackToolCall(fn, status, durationMs, workspace, errorMsg);
1008
+ if (!shouldLogAudit(status)) return;
1009
+ const base = `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms${budgetMs !== void 0 ? ` budget=${budgetMs}ms` : ""}${timedOut ? " timedOut=true" : ""}`;
1010
+ process.stderr.write(
1011
+ status === "error" && errorMsg ? `${base} error=${JSON.stringify(errorMsg)}
1012
+ ` : `${base}
1013
+ `
1014
+ );
1015
+ }
1016
+
508
1017
  // src/client.ts
509
1018
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
510
1019
 
@@ -546,6 +1055,29 @@ function recordToolAction(tool, action) {
546
1055
  toolActionCounts.set(key, (toolActionCounts.get(key) ?? 0) + 1);
547
1056
  }
548
1057
 
1058
+ // src/lib/deploymentUrlResolver.ts
1059
+ function parseFallbackUrls(raw) {
1060
+ if (!raw) return [];
1061
+ return raw.split(",").map((u) => u.trim()).filter(Boolean);
1062
+ }
1063
+ async function probeDeploymentCandidates(candidates, apiKey) {
1064
+ for (const candidate of candidates) {
1065
+ try {
1066
+ const probeRes = await fetch(`${candidate}/api/key-check`, {
1067
+ method: "POST",
1068
+ headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
1069
+ signal: AbortSignal.timeout(3e3)
1070
+ });
1071
+ if (probeRes.ok) {
1072
+ const data = await probeRes.json();
1073
+ if (data.ok) return candidate;
1074
+ }
1075
+ } catch {
1076
+ }
1077
+ }
1078
+ return null;
1079
+ }
1080
+
549
1081
  // src/client.ts
550
1082
  var _conversationId;
551
1083
  function getConversationId() {
@@ -565,30 +1097,6 @@ function getToolContext() {
565
1097
  return toolContextStore.getStore() ?? null;
566
1098
  }
567
1099
  var DEFAULT_CLOUD_URL = "https://gateway.productbrain.io";
568
- var KernelCallError = class extends Error {
569
- status;
570
- code;
571
- /** WP-316 S1a: Structured commit validation — required field keys missing from entry.data. */
572
- missingRequiredFields;
573
- /** WP-316 S1a: Structured commit validation — field-level data errors. */
574
- fieldErrors;
575
- /**
576
- * WP-465 slice ⑤: structured diagnostics carried by an `ok:false` kernel envelope
577
- * (e.g. `coherencyRefusals`, `blockers`). The gateway forwards these verbatim at
578
- * HTTP 200; without preserving them here a refused/blocked envelope would collapse
579
- * into a bare code+message and the caller could not surface the per-offender routes.
580
- */
581
- diagnostics;
582
- constructor(message, status, code, missingRequiredFields, fieldErrors, diagnostics) {
583
- super(message);
584
- this.name = "KernelCallError";
585
- this.status = status;
586
- this.code = code;
587
- this.missingRequiredFields = missingRequiredFields;
588
- this.fieldErrors = fieldErrors;
589
- this.diagnostics = diagnostics;
590
- }
591
- };
592
1100
  var CACHE_TTL_MS = 6e4;
593
1101
  var CACHEABLE_FNS = [
594
1102
  "chain.getOrientEntries",
@@ -775,8 +1283,6 @@ async function recordSessionActivity(activity) {
775
1283
  } catch {
776
1284
  }
777
1285
  }
778
- var AUDIT_BUFFER_SIZE = 50;
779
- var auditBuffer = [];
780
1286
  function bootstrap() {
781
1287
  const explicit = process.env.CONVEX_SITE_URL ?? process.env.PRODUCTBRAIN_URL;
782
1288
  process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
@@ -793,16 +1299,11 @@ function bootstrapHttp() {
793
1299
  process.env.CONVEX_SITE_URL ??= process.env.PRODUCTBRAIN_URL ?? DEFAULT_CLOUD_URL;
794
1300
  warnOnProdFallthrough(process.env.CONVEX_SITE_URL, { explicit: explicit != null });
795
1301
  }
796
- function parseFallbackUrls() {
797
- const raw = process.env.CONVEX_FALLBACK_URLS;
798
- if (!raw) return [];
799
- return raw.split(",").map((u) => u.trim()).filter(Boolean);
800
- }
801
1302
  async function resolveDeploymentUrl() {
802
1303
  const s = state();
803
1304
  if (s.deploymentUrl) return s.deploymentUrl;
804
1305
  const primaryUrl = (process.env.CONVEX_SITE_URL ?? DEFAULT_CLOUD_URL).replace(/\/$/, "");
805
- const fallbacks = parseFallbackUrls();
1306
+ const fallbacks = parseFallbackUrls(process.env.CONVEX_FALLBACK_URLS);
806
1307
  if (fallbacks.length === 0) {
807
1308
  return primaryUrl;
808
1309
  }
@@ -813,50 +1314,24 @@ async function resolveDeploymentUrl() {
813
1314
  } catch {
814
1315
  return primaryUrl;
815
1316
  }
816
- for (const candidate of candidates) {
817
- try {
818
- const probeRes = await fetch(`${candidate}/api/key-check`, {
819
- method: "POST",
820
- headers: { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json" },
821
- signal: AbortSignal.timeout(3e3)
822
- });
823
- if (probeRes.ok) {
824
- const data = await probeRes.json();
825
- if (data.ok) {
826
- s.deploymentUrl = candidate;
827
- return candidate;
828
- }
829
- }
830
- } catch {
831
- }
1317
+ const found = await probeDeploymentCandidates(candidates, apiKey);
1318
+ if (found) {
1319
+ s.deploymentUrl = found;
1320
+ return found;
832
1321
  }
833
1322
  return candidates[0];
834
1323
  }
835
- function shouldLogAudit(status) {
836
- return status === "error" || process.env.MCP_DEBUG === "1";
837
- }
838
- function audit(fn, status, durationMs, errorMsg) {
839
- const ts = (/* @__PURE__ */ new Date()).toISOString();
840
- const workspace = state().workspaceId ?? "unresolved";
841
- const toolCtx = getToolContext();
842
- const entry = { ts, fn, workspace, status, durationMs };
843
- if (errorMsg) entry.error = errorMsg;
844
- if (toolCtx) entry.toolContext = toolCtx;
845
- auditBuffer.push(entry);
846
- if (auditBuffer.length > AUDIT_BUFFER_SIZE) auditBuffer.shift();
847
- trackToolCall(fn, status, durationMs, workspace, errorMsg);
848
- if (!shouldLogAudit(status)) return;
849
- const base = `[MCP-AUDIT] ${ts} fn=${fn} workspace=${workspace} status=${status} duration=${durationMs}ms`;
850
- if (status === "error" && errorMsg) {
851
- process.stderr.write(`${base} error=${JSON.stringify(errorMsg)}
852
- `);
853
- } else {
854
- process.stderr.write(`${base}
855
- `);
856
- }
857
- }
858
- function getAuditLog() {
859
- return auditBuffer;
1324
+ function audit(fn, status, durationMs, errorMsg, meta) {
1325
+ recordGatewayCall({
1326
+ fn,
1327
+ status,
1328
+ durationMs,
1329
+ errorMsg,
1330
+ workspace: state().workspaceId ?? cacheScope(),
1331
+ toolContext: getToolContext(),
1332
+ budgetMs: meta?.budgetMs,
1333
+ timedOut: meta?.timedOut
1334
+ });
860
1335
  }
861
1336
  var TOUCH_EXCLUDED = /* @__PURE__ */ new Set([
862
1337
  "agent.touchSession",
@@ -874,12 +1349,13 @@ var MCP_TELEMETRY_SOURCE = "mcp";
874
1349
  async function callGateway(fn, args) {
875
1350
  const siteUrl = await resolveDeploymentUrl();
876
1351
  const apiKey = getActiveApiKey();
1352
+ const budgetMs = latencyBudgetMsForRoute(fn);
877
1353
  const start = Date.now();
878
1354
  let res;
879
1355
  try {
880
1356
  res = await fetch(`${siteUrl}/api/aki`, {
881
1357
  method: "POST",
882
- signal: AbortSignal.timeout(1e4),
1358
+ signal: AbortSignal.timeout(budgetMs),
883
1359
  headers: {
884
1360
  "Content-Type": "application/json",
885
1361
  Authorization: `Bearer ${apiKey}`,
@@ -890,14 +1366,34 @@ async function callGateway(fn, args) {
890
1366
  body: JSON.stringify({ fn, args })
891
1367
  });
892
1368
  } catch (err) {
893
- audit(fn, "error", Date.now() - start, err.message);
894
- throw new Error(`MCP call "${fn}" network error: ${err.message}`);
1369
+ throwClassifiedGatewayFailure(
1370
+ err,
1371
+ fn,
1372
+ args,
1373
+ budgetMs,
1374
+ Date.now() - start,
1375
+ "network",
1376
+ (m, t) => audit(fn, "error", Date.now() - start, m, { budgetMs, timedOut: t })
1377
+ );
1378
+ }
1379
+ let json;
1380
+ try {
1381
+ json = await res.json();
1382
+ } catch (err) {
1383
+ json = emptyBodyOrThrowClassified(
1384
+ err,
1385
+ fn,
1386
+ args,
1387
+ budgetMs,
1388
+ Date.now() - start,
1389
+ res.ok,
1390
+ (m, t) => audit(fn, "error", Date.now() - start, m, { budgetMs, timedOut: t })
1391
+ );
895
1392
  }
896
- const json = await res.json();
897
1393
  if (!res.ok || json.ok === false) {
898
1394
  const errJson = json;
899
1395
  const msg = errJson.error ?? errJson.message ?? "unknown error";
900
- audit(fn, "error", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg);
1396
+ audit(fn, "error", Date.now() - start, errJson.code ? `${msg} [${errJson.code}]` : msg, { budgetMs });
901
1397
  throw new KernelCallError(
902
1398
  `MCP call "${fn}" failed (${res.status}): ${msg}`,
903
1399
  res.status,
@@ -907,7 +1403,7 @@ async function callGateway(fn, args) {
907
1403
  errJson.diagnostics && typeof errJson.diagnostics === "object" ? errJson.diagnostics : void 0
908
1404
  );
909
1405
  }
910
- audit(fn, "ok", Date.now() - start);
1406
+ audit(fn, "ok", Date.now() - start, void 0, { budgetMs });
911
1407
  const { data, summary, next, _meta } = json;
912
1408
  return {
913
1409
  data,
@@ -971,7 +1467,7 @@ async function resolveWorkspaceWithRetry(maxRetries = 2) {
971
1467
  return s.workspaceId;
972
1468
  } catch (err) {
973
1469
  lastError = err;
974
- const isTransient = /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);
1470
+ const isTransient = err?.name === "GatewayTimeoutError" || /network error|fetch failed|ECONNREFUSED|ETIMEDOUT/i.test(err.message);
975
1471
  if (!isTransient || attempt === maxRetries) break;
976
1472
  const delay = 1e3 * (attempt + 1);
977
1473
  process.stderr.write(
@@ -1127,6 +1623,10 @@ export {
1127
1623
  trackClassifierDivergence,
1128
1624
  getPostHogClient,
1129
1625
  shutdownAnalytics,
1626
+ getAuditLog,
1627
+ getMergedGatewaySeamCounters,
1628
+ formatGatewaySeamSummary,
1629
+ KernelCallError,
1130
1630
  hashKey,
1131
1631
  runWithAuth,
1132
1632
  getRequestApiKey,
@@ -1138,7 +1638,6 @@ export {
1138
1638
  getConversationId,
1139
1639
  runWithToolContext,
1140
1640
  DEFAULT_CLOUD_URL,
1141
- KernelCallError,
1142
1641
  cacheScope,
1143
1642
  getAgentSessionId,
1144
1643
  isSessionOriented,
@@ -1150,7 +1649,6 @@ export {
1150
1649
  recordSessionActivity,
1151
1650
  bootstrap,
1152
1651
  bootstrapHttp,
1153
- getAuditLog,
1154
1652
  kernelCall,
1155
1653
  kernelCallEnvelope,
1156
1654
  getWorkspaceId,
@@ -1163,4 +1661,4 @@ export {
1163
1661
  requireVendorTriageAccess,
1164
1662
  recoverSessionState
1165
1663
  };
1166
- //# sourceMappingURL=chunk-U7YKAA66.js.map
1664
+ //# sourceMappingURL=chunk-VCTFH2U7.js.map