@danielblomma/cortex-mcp 2.4.0 → 2.4.1

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 (173) hide show
  1. package/CHANGELOG.md +201 -0
  2. package/README.md +19 -3
  3. package/bin/cortex.mjs +308 -75
  4. package/bin/daemon-control.mjs +162 -0
  5. package/package.json +7 -3
  6. package/scaffold/mcp/dist/.cortex-build-hash +1 -0
  7. package/scaffold/mcp/dist/cli/enterprise-setup.js +134 -0
  8. package/scaffold/mcp/dist/cli/govern.js +937 -0
  9. package/scaffold/mcp/dist/cli/query.js +409 -0
  10. package/scaffold/mcp/dist/cli/run.js +295 -0
  11. package/scaffold/mcp/dist/cli/stage.js +308 -0
  12. package/scaffold/mcp/dist/cli/telemetry-test.js +141 -0
  13. package/scaffold/mcp/dist/cli/ungoverned-detector.js +133 -0
  14. package/scaffold/mcp/dist/contextEntities.js +282 -0
  15. package/scaffold/mcp/dist/core/audit/query.js +72 -0
  16. package/scaffold/mcp/dist/core/audit/writer.js +28 -0
  17. package/scaffold/mcp/dist/core/config.js +235 -0
  18. package/scaffold/mcp/dist/core/enterprise-host-identity.js +193 -0
  19. package/scaffold/mcp/dist/core/enterprise-identity.js +18 -0
  20. package/scaffold/mcp/dist/core/enterprise-rotation.js +158 -0
  21. package/scaffold/mcp/dist/core/govern-paths.js +21 -0
  22. package/scaffold/mcp/dist/core/index.js +14 -0
  23. package/scaffold/mcp/dist/core/license.js +278 -0
  24. package/scaffold/mcp/dist/core/policy/enforce.js +66 -0
  25. package/scaffold/mcp/dist/core/policy/injection.js +172 -0
  26. package/scaffold/mcp/dist/core/policy/store.js +179 -0
  27. package/scaffold/mcp/dist/core/rbac/check.js +30 -0
  28. package/scaffold/mcp/dist/core/secure-endpoint.js +25 -0
  29. package/scaffold/mcp/dist/core/telemetry/collector.js +285 -0
  30. package/scaffold/mcp/dist/core/telemetry/state-dir.js +31 -0
  31. package/scaffold/mcp/dist/core/validators/builtins.js +632 -0
  32. package/scaffold/mcp/dist/core/validators/config.js +44 -0
  33. package/scaffold/mcp/dist/core/validators/engine.js +120 -0
  34. package/scaffold/mcp/dist/core/validators/evaluators/code_comments.js +236 -0
  35. package/scaffold/mcp/dist/core/validators/evaluators/regex.js +116 -0
  36. package/scaffold/mcp/dist/core/workflow/artifact-io.js +103 -0
  37. package/scaffold/mcp/dist/core/workflow/capabilities.js +88 -0
  38. package/scaffold/mcp/dist/core/workflow/default-workflows.js +102 -0
  39. package/scaffold/mcp/dist/core/workflow/enforcement.js +164 -0
  40. package/scaffold/mcp/dist/core/workflow/envelope.js +132 -0
  41. package/scaffold/mcp/dist/core/workflow/index.js +11 -0
  42. package/scaffold/mcp/dist/core/workflow/mcp-tools.js +175 -0
  43. package/scaffold/mcp/dist/core/workflow/resolution.js +69 -0
  44. package/scaffold/mcp/dist/core/workflow/run-lifecycle.js +123 -0
  45. package/scaffold/mcp/dist/core/workflow/schemas.js +141 -0
  46. package/scaffold/mcp/dist/core/workflow/synced-capability-registry.js +60 -0
  47. package/scaffold/mcp/dist/core/workflow/synced-registry.js +60 -0
  48. package/scaffold/mcp/dist/daemon/capability-sync-checker.js +242 -0
  49. package/scaffold/mcp/dist/daemon/client.js +130 -0
  50. package/scaffold/mcp/dist/daemon/egress-proxy.js +288 -0
  51. package/scaffold/mcp/dist/daemon/global-host-events.js +222 -0
  52. package/scaffold/mcp/dist/daemon/heartbeat-pusher.js +116 -0
  53. package/scaffold/mcp/dist/daemon/heartbeat-tracker.js +165 -0
  54. package/scaffold/mcp/dist/daemon/host-events-pusher.js +249 -0
  55. package/scaffold/mcp/dist/daemon/main.js +449 -0
  56. package/scaffold/mcp/dist/daemon/paths.js +36 -0
  57. package/scaffold/mcp/dist/daemon/project-service-registry.js +78 -0
  58. package/scaffold/mcp/dist/daemon/protocol.js +8 -0
  59. package/scaffold/mcp/dist/daemon/server.js +180 -0
  60. package/scaffold/mcp/dist/daemon/skill-sync-checker.js +557 -0
  61. package/scaffold/mcp/dist/daemon/sync-checker.js +165 -0
  62. package/scaffold/mcp/dist/daemon/ungoverned-scanner.js +136 -0
  63. package/scaffold/mcp/dist/daemon/workflow-sync-checker.js +249 -0
  64. package/scaffold/mcp/dist/defaults.js +6 -0
  65. package/scaffold/mcp/dist/embed.js +627 -0
  66. package/scaffold/mcp/dist/embedScheduler.js +479 -0
  67. package/scaffold/mcp/dist/embeddings.js +167 -0
  68. package/scaffold/mcp/dist/enterprise/audit/push.js +66 -0
  69. package/scaffold/mcp/dist/enterprise/index.js +327 -0
  70. package/scaffold/mcp/dist/enterprise/model/deploy.js +27 -0
  71. package/scaffold/mcp/dist/enterprise/policy/sync.js +129 -0
  72. package/scaffold/mcp/dist/enterprise/privacy/boundary.js +184 -0
  73. package/scaffold/mcp/dist/enterprise/reviews/changed-files.js +33 -0
  74. package/scaffold/mcp/dist/enterprise/reviews/pattern-context.js +202 -0
  75. package/scaffold/mcp/dist/enterprise/reviews/policy-selection.js +46 -0
  76. package/scaffold/mcp/dist/enterprise/reviews/push.js +102 -0
  77. package/scaffold/mcp/dist/enterprise/reviews/trust-state.js +186 -0
  78. package/scaffold/mcp/dist/enterprise/telemetry/sync.js +57 -0
  79. package/scaffold/mcp/dist/enterprise/tools/enterprise.js +826 -0
  80. package/scaffold/mcp/dist/enterprise/tools/harness.js +40 -0
  81. package/scaffold/mcp/dist/enterprise/tools/walk.js +73 -0
  82. package/scaffold/mcp/dist/enterprise/violations/push.js +77 -0
  83. package/scaffold/mcp/dist/enterprise/workflow/push.js +44 -0
  84. package/scaffold/mcp/dist/enterprise/workflow/state.js +329 -0
  85. package/scaffold/mcp/dist/frontmatter.js +33 -0
  86. package/scaffold/mcp/dist/graph.js +769 -0
  87. package/scaffold/mcp/dist/graphCsv.js +55 -0
  88. package/scaffold/mcp/dist/graphMetrics.js +8 -0
  89. package/scaffold/mcp/dist/hooks/permission-request.js +89 -0
  90. package/scaffold/mcp/dist/hooks/post-tool-use.js +105 -0
  91. package/scaffold/mcp/dist/hooks/pre-compact.js +29 -0
  92. package/scaffold/mcp/dist/hooks/pre-tool-use.js +78 -0
  93. package/scaffold/mcp/dist/hooks/session-end.js +43 -0
  94. package/scaffold/mcp/dist/hooks/session-start.js +41 -0
  95. package/scaffold/mcp/dist/hooks/shared.js +194 -0
  96. package/scaffold/mcp/dist/hooks/stop.js +28 -0
  97. package/scaffold/mcp/dist/hooks/user-prompt-submit.js +33 -0
  98. package/scaffold/mcp/dist/impactPresentation.js +137 -0
  99. package/scaffold/mcp/dist/impactRanking.js +191 -0
  100. package/scaffold/mcp/dist/impactResponse.js +30 -0
  101. package/scaffold/mcp/dist/impactResults.js +105 -0
  102. package/scaffold/mcp/dist/impactSeed.js +20 -0
  103. package/scaffold/mcp/dist/impactTraversal.js +64 -0
  104. package/scaffold/mcp/dist/jsonl.js +77 -0
  105. package/scaffold/mcp/dist/loadGraph.js +759 -0
  106. package/scaffold/mcp/dist/lruCache.js +38 -0
  107. package/scaffold/mcp/dist/paths.js +97 -0
  108. package/scaffold/mcp/dist/patternEvidence.js +272 -0
  109. package/scaffold/mcp/dist/plugin.js +81 -0
  110. package/scaffold/mcp/dist/presets.js +78 -0
  111. package/scaffold/mcp/dist/relatedResponse.js +18 -0
  112. package/scaffold/mcp/dist/relatedTraversal.js +78 -0
  113. package/scaffold/mcp/dist/rules.js +23 -0
  114. package/scaffold/mcp/dist/search.js +212 -0
  115. package/scaffold/mcp/dist/searchCore.js +457 -0
  116. package/scaffold/mcp/dist/searchResults.js +230 -0
  117. package/scaffold/mcp/dist/server.js +317 -0
  118. package/scaffold/mcp/dist/types.js +1 -0
  119. package/scaffold/mcp/package-lock.json +321 -200
  120. package/scaffold/mcp/package.json +15 -6
  121. package/scaffold/mcp/src/cli/enterprise-setup.ts +82 -8
  122. package/scaffold/mcp/src/cli/govern.ts +137 -52
  123. package/scaffold/mcp/src/cli/run.ts +53 -19
  124. package/scaffold/mcp/src/cli/stage.ts +8 -3
  125. package/scaffold/mcp/src/core/config.ts +9 -2
  126. package/scaffold/mcp/src/core/enterprise-host-identity.ts +259 -0
  127. package/scaffold/mcp/src/core/enterprise-identity.ts +20 -0
  128. package/scaffold/mcp/src/core/enterprise-rotation.ts +185 -0
  129. package/scaffold/mcp/src/core/govern-paths.ts +30 -0
  130. package/scaffold/mcp/src/core/license.ts +186 -17
  131. package/scaffold/mcp/src/core/secure-endpoint.ts +23 -0
  132. package/scaffold/mcp/src/core/workflow/enforcement.ts +8 -1
  133. package/scaffold/mcp/src/core/workflow/mcp-tools.ts +3 -0
  134. package/scaffold/mcp/src/core/workflow/resolution.ts +9 -1
  135. package/scaffold/mcp/src/core/workflow/synced-capability-registry.ts +15 -0
  136. package/scaffold/mcp/src/core/workflow/synced-registry.ts +15 -0
  137. package/scaffold/mcp/src/daemon/capability-sync-checker.ts +38 -2
  138. package/scaffold/mcp/src/daemon/egress-proxy.ts +14 -8
  139. package/scaffold/mcp/src/daemon/global-host-events.ts +288 -0
  140. package/scaffold/mcp/src/daemon/heartbeat-pusher.ts +4 -0
  141. package/scaffold/mcp/src/daemon/heartbeat-tracker.ts +2 -0
  142. package/scaffold/mcp/src/daemon/host-events-pusher.ts +5 -0
  143. package/scaffold/mcp/src/daemon/main.ts +99 -25
  144. package/scaffold/mcp/src/daemon/project-service-registry.ts +107 -0
  145. package/scaffold/mcp/src/daemon/skill-sync-checker.ts +368 -31
  146. package/scaffold/mcp/src/daemon/sync-checker.ts +4 -0
  147. package/scaffold/mcp/src/daemon/ungoverned-scanner.ts +57 -25
  148. package/scaffold/mcp/src/daemon/workflow-sync-checker.ts +41 -2
  149. package/scaffold/mcp/src/enterprise/audit/push.ts +8 -0
  150. package/scaffold/mcp/src/enterprise/policy/sync.ts +12 -0
  151. package/scaffold/mcp/src/enterprise/reviews/push.ts +9 -0
  152. package/scaffold/mcp/src/enterprise/reviews/trust-state.ts +3 -1
  153. package/scaffold/mcp/src/enterprise/telemetry/sync.ts +9 -0
  154. package/scaffold/mcp/src/enterprise/violations/push.ts +9 -0
  155. package/scaffold/mcp/src/enterprise/workflow/push.ts +7 -0
  156. package/scaffold/mcp/src/plugin.ts +20 -0
  157. package/scaffold/mcp/tests/copilot-shim.test.mjs +19 -1
  158. package/scaffold/mcp/tests/egress-proxy.test.mjs +37 -0
  159. package/scaffold/mcp/tests/enterprise-identity-sync.test.mjs +401 -0
  160. package/scaffold/mcp/tests/enterprise-setup.test.mjs +189 -0
  161. package/scaffold/mcp/tests/global-host-events.test.mjs +81 -0
  162. package/scaffold/mcp/tests/govern-install.test.mjs +95 -2
  163. package/scaffold/mcp/tests/govern-repair.test.mjs +20 -3
  164. package/scaffold/mcp/tests/heartbeat-tracker.test.mjs +26 -0
  165. package/scaffold/mcp/tests/license.test.mjs +367 -0
  166. package/scaffold/mcp/tests/project-service-registry.test.mjs +172 -0
  167. package/scaffold/mcp/tests/review-trust-contract.test.mjs +18 -0
  168. package/scaffold/mcp/tests/secure-enterprise-endpoint.test.mjs +46 -0
  169. package/scaffold/mcp/tests/skill-sync-checker.test.mjs +446 -2
  170. package/scaffold/mcp/tests/ungoverned-scanner.test.mjs +104 -22
  171. package/scaffold/mcp/tests/workflow-cli.test.mjs +14 -1
  172. package/scaffold/mcp/tests/workflow-synced-capabilities.test.mjs +37 -0
  173. package/scaffold/mcp/tests/workflow-synced-registry.test.mjs +40 -1
@@ -0,0 +1,186 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { setWorkflowReviewTrust } from "../workflow/state.js";
4
+ import { resolveWorkflowDefinition } from "../../core/workflow/resolution.js";
5
+ function trustStatePath(contextDir) {
6
+ return join(contextDir, "review-trust.json");
7
+ }
8
+ function nowIso() {
9
+ return new Date().toISOString();
10
+ }
11
+ function initialTrustState() {
12
+ return {
13
+ version: 1,
14
+ repo: null,
15
+ instance_id: null,
16
+ session_id: null,
17
+ workflow: {
18
+ id: null,
19
+ source: "unknown",
20
+ task_id: null,
21
+ },
22
+ last_review_at: null,
23
+ review_summary: null,
24
+ skipped_policies: [],
25
+ delivery: {
26
+ status: "never_attempted",
27
+ review_count: 0,
28
+ queued_at: null,
29
+ last_attempted_at: null,
30
+ last_pushed_at: null,
31
+ last_accepted_at: null,
32
+ last_error: null,
33
+ },
34
+ trust_warnings: [],
35
+ };
36
+ }
37
+ function ensureContextDir(contextDir) {
38
+ mkdirSync(contextDir, { recursive: true });
39
+ }
40
+ function inferWorkflow(projectRoot, taskId) {
41
+ const normalizedTaskId = taskId?.trim();
42
+ if (!normalizedTaskId) {
43
+ return { id: null, source: "unknown", task_id: null };
44
+ }
45
+ const statePath = join(projectRoot, ".agents", normalizedTaskId, "state.json");
46
+ if (!existsSync(statePath)) {
47
+ return { id: null, source: "unknown", task_id: normalizedTaskId };
48
+ }
49
+ try {
50
+ const raw = JSON.parse(readFileSync(statePath, "utf8"));
51
+ const workflowId = typeof raw.workflow_id === "string" ? raw.workflow_id : null;
52
+ if (!workflowId) {
53
+ return { id: null, source: "unknown", task_id: normalizedTaskId };
54
+ }
55
+ const resolved = resolveWorkflowDefinition(workflowId, {
56
+ cwd: projectRoot,
57
+ });
58
+ return {
59
+ id: workflowId,
60
+ source: resolved.source,
61
+ task_id: normalizedTaskId,
62
+ };
63
+ }
64
+ catch {
65
+ return {
66
+ id: null,
67
+ source: "unknown",
68
+ task_id: normalizedTaskId,
69
+ };
70
+ }
71
+ }
72
+ function computeWarnings(state) {
73
+ const warnings = [];
74
+ if (!state.workflow.id) {
75
+ warnings.push({
76
+ code: "workflow-unresolved",
77
+ message: "No active local workflow could be resolved for this review session, so workflow/review correlation is unproven.",
78
+ });
79
+ }
80
+ else if (state.workflow.source !== "synced") {
81
+ warnings.push({
82
+ code: "workflow-source-unverified",
83
+ message: `Workflow "${state.workflow.id}" is not proven to come from the synced org registry for this session.`,
84
+ });
85
+ }
86
+ if (state.delivery.status === "queued" || state.delivery.status === "pushed") {
87
+ warnings.push({
88
+ code: "review-delivery-pending",
89
+ message: "Local review results exist for this session, but Cortex has not yet confirmed acceptance by the control plane.",
90
+ });
91
+ }
92
+ if (state.delivery.status === "failed") {
93
+ warnings.push({
94
+ code: "review-delivery-failed",
95
+ message: `Review delivery failed${state.delivery.last_error ? `: ${state.delivery.last_error}` : ""}`,
96
+ });
97
+ }
98
+ if (state.skipped_policies.length > 0) {
99
+ warnings.push({
100
+ code: "policy-not-enforceable",
101
+ message: `${state.skipped_policies.length} configured policy` +
102
+ `${state.skipped_policies.length === 1 ? " was" : "ies were"} not enforceable locally.`,
103
+ });
104
+ }
105
+ return warnings;
106
+ }
107
+ export function loadReviewTrustState(contextDir) {
108
+ ensureContextDir(contextDir);
109
+ const filePath = trustStatePath(contextDir);
110
+ if (!existsSync(filePath)) {
111
+ const initial = initialTrustState();
112
+ writeReviewTrustState(contextDir, initial);
113
+ return initial;
114
+ }
115
+ try {
116
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
117
+ parsed.trust_warnings = computeWarnings(parsed);
118
+ return parsed;
119
+ }
120
+ catch {
121
+ const initial = initialTrustState();
122
+ writeReviewTrustState(contextDir, initial);
123
+ return initial;
124
+ }
125
+ }
126
+ export function writeReviewTrustState(contextDir, state) {
127
+ ensureContextDir(contextDir);
128
+ state.trust_warnings = computeWarnings(state);
129
+ writeFileSync(trustStatePath(contextDir), `${JSON.stringify(state, null, 2)}\n`, "utf8");
130
+ setWorkflowReviewTrust(contextDir, state);
131
+ return state;
132
+ }
133
+ export function recordQueuedReviewTrustState(context, input) {
134
+ const state = loadReviewTrustState(context.contextDir);
135
+ const workflow = inferWorkflow(context.projectRoot, context.task_id);
136
+ state.repo = context.repo ?? state.repo;
137
+ state.instance_id = context.instance_id ?? state.instance_id;
138
+ state.session_id = context.session_id ?? state.session_id;
139
+ state.workflow = workflow;
140
+ state.last_review_at = input.reviewedAt;
141
+ state.review_summary = {
142
+ total: input.summary.total,
143
+ passed: input.summary.passed,
144
+ failed: input.summary.failed,
145
+ warnings: input.summary.warnings,
146
+ skipped: input.summary.skipped,
147
+ };
148
+ state.skipped_policies = input.skippedPolicies;
149
+ const hasQueuedReviews = input.summary.total > 0;
150
+ state.delivery = {
151
+ status: hasQueuedReviews ? "queued" : "never_attempted",
152
+ review_count: input.summary.total,
153
+ queued_at: hasQueuedReviews ? input.reviewedAt : null,
154
+ last_attempted_at: null,
155
+ last_pushed_at: null,
156
+ last_accepted_at: null,
157
+ last_error: null,
158
+ };
159
+ return writeReviewTrustState(context.contextDir, state);
160
+ }
161
+ export function recordReviewDeliveryStatus(contextDir, status, input = {}) {
162
+ const state = loadReviewTrustState(contextDir);
163
+ const at = input.attemptedAt ?? nowIso();
164
+ state.delivery.review_count = input.reviewCount ?? state.delivery.review_count;
165
+ state.delivery.last_attempted_at = at;
166
+ if (status === "pushed") {
167
+ state.delivery.status = "pushed";
168
+ state.delivery.last_pushed_at = at;
169
+ state.delivery.last_error = null;
170
+ }
171
+ else if (status === "accepted") {
172
+ state.delivery.status = "accepted";
173
+ state.delivery.last_pushed_at = at;
174
+ state.delivery.last_accepted_at = at;
175
+ state.delivery.last_error = null;
176
+ }
177
+ else if (status === "failed") {
178
+ state.delivery.status = "failed";
179
+ state.delivery.last_pushed_at = at;
180
+ state.delivery.last_error = input.error ?? "unknown error";
181
+ }
182
+ else {
183
+ state.delivery.status = status;
184
+ }
185
+ return writeReviewTrustState(contextDir, state);
186
+ }
@@ -0,0 +1,57 @@
1
+ import { isAllowedEnterpriseEndpoint } from "../../core/secure-endpoint.js";
2
+ import { buildTelemetryPushPayload } from "../privacy/boundary.js";
3
+ let lastPush = null;
4
+ export function getLastPush() {
5
+ return lastPush;
6
+ }
7
+ /**
8
+ * Push aggregated metrics to the Cortex Cloud API.
9
+ * Connected edition only. Returns success/failure.
10
+ *
11
+ * The actual cloud API is built in Phase 4 — this sends a POST
12
+ * with JSON body and expects a 2xx response.
13
+ */
14
+ export async function pushMetrics(metrics, endpoint, apiKey, context = {}) {
15
+ if (!endpoint || !apiKey) {
16
+ const result = { success: false, error: "endpoint or api_key not configured" };
17
+ lastPush = result;
18
+ return result;
19
+ }
20
+ if (!isAllowedEnterpriseEndpoint(endpoint)) {
21
+ const result = {
22
+ success: false,
23
+ error: "insecure or invalid enterprise endpoint",
24
+ };
25
+ lastPush = result;
26
+ return result;
27
+ }
28
+ try {
29
+ const response = await fetch(endpoint, {
30
+ method: "POST",
31
+ headers: {
32
+ "Content-Type": "application/json",
33
+ "Authorization": `Bearer ${apiKey}`,
34
+ },
35
+ body: JSON.stringify(buildTelemetryPushPayload(metrics, context)),
36
+ signal: AbortSignal.timeout(10000),
37
+ });
38
+ const result = {
39
+ success: response.ok,
40
+ status: response.status,
41
+ pushed_at: new Date().toISOString(),
42
+ };
43
+ if (!response.ok) {
44
+ result.error = `HTTP ${response.status}`;
45
+ }
46
+ lastPush = result;
47
+ return result;
48
+ }
49
+ catch (err) {
50
+ const result = {
51
+ success: false,
52
+ error: err instanceof Error ? err.message : "unknown error",
53
+ };
54
+ lastPush = result;
55
+ return result;
56
+ }
57
+ }