@kylewadegrove/cutline-mcp-cli 0.7.0 → 0.7.2
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/auth/keychain.js +10 -6
- package/dist/commands/setup.js +9 -13
- package/dist/servers/{chunk-XKS3J74N.js → chunk-ZVWDXO6M.js} +9 -12
- package/dist/servers/cutline-server.js +27 -598
- package/dist/servers/{data-client-7MEIYP5S.js → data-client-FPUZBUO3.js} +5 -1
- package/dist/servers/exploration-server.js +1 -1
- package/dist/servers/integrations-server.js +1 -1
- package/dist/servers/output-server.js +1 -1
- package/dist/servers/premortem-server.js +1 -1
- package/dist/servers/tools-server.js +1 -1
- package/package.json +1 -4
package/dist/auth/keychain.js
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
const SERVICE_NAME = 'cutline-mcp';
|
|
3
|
-
const ACCOUNT_NAME = 'refresh-token';
|
|
1
|
+
import { saveConfig, loadConfig } from '../utils/config-store.js';
|
|
4
2
|
export async function storeRefreshToken(token) {
|
|
5
|
-
|
|
3
|
+
saveConfig({ refreshToken: token });
|
|
6
4
|
}
|
|
7
5
|
export async function getRefreshToken() {
|
|
8
|
-
|
|
6
|
+
const config = loadConfig();
|
|
7
|
+
return config.refreshToken || null;
|
|
9
8
|
}
|
|
10
9
|
export async function deleteRefreshToken() {
|
|
11
|
-
|
|
10
|
+
const config = loadConfig();
|
|
11
|
+
if (!config.refreshToken)
|
|
12
|
+
return false;
|
|
13
|
+
delete config.refreshToken;
|
|
14
|
+
saveConfig(config);
|
|
15
|
+
return true;
|
|
12
16
|
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -200,21 +200,17 @@ export async function setupCommand(options) {
|
|
|
200
200
|
const home = homedir();
|
|
201
201
|
const ideConfigs = [
|
|
202
202
|
{ name: 'Cursor', path: join(home, '.cursor', 'mcp.json') },
|
|
203
|
-
{ name: 'Claude Code', path: join(home, '.claude
|
|
203
|
+
{ name: 'Claude Code', path: join(home, '.claude.json') },
|
|
204
204
|
];
|
|
205
205
|
let wroteAny = false;
|
|
206
206
|
for (const ide of ideConfigs) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
console.log(chalk.red(` ✗ ${ide.name}`), chalk.dim(err.message));
|
|
217
|
-
}
|
|
207
|
+
try {
|
|
208
|
+
mergeIdeConfig(ide.path, serverConfig);
|
|
209
|
+
console.log(chalk.green(` ✓ ${ide.name}`), chalk.dim(ide.path));
|
|
210
|
+
wroteAny = true;
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
console.log(chalk.red(` ✗ ${ide.name}`), chalk.dim(err.message));
|
|
218
214
|
}
|
|
219
215
|
}
|
|
220
216
|
if (wroteAny) {
|
|
@@ -230,7 +226,7 @@ export async function setupCommand(options) {
|
|
|
230
226
|
await initCommand({ projectRoot: options.projectRoot, staging: options.staging });
|
|
231
227
|
// ── 5. Claude Code one-liners ────────────────────────────────────────────
|
|
232
228
|
console.log(chalk.bold(' Claude Code one-liner alternative:\n'));
|
|
233
|
-
console.log(chalk.dim(' If you prefer `claude mcp add` instead of
|
|
229
|
+
console.log(chalk.dim(' If you prefer `claude mcp add` instead of ~/.claude.json:\n'));
|
|
234
230
|
const coreServers = ['constraints', 'premortem', 'tools', 'exploration'];
|
|
235
231
|
for (const name of coreServers) {
|
|
236
232
|
console.log(chalk.cyan(` claude mcp add cutline-${name} -- npx -y @kylewadegrove/cutline-mcp-cli serve ${name}`));
|
|
@@ -330,18 +330,6 @@ async function getStoredToken() {
|
|
|
330
330
|
} catch (e) {
|
|
331
331
|
console.error("[MCP Auth] Failed to read config file:", e);
|
|
332
332
|
}
|
|
333
|
-
try {
|
|
334
|
-
console.error("[MCP Auth Debug] Attempting to import keytar...");
|
|
335
|
-
const keytar = await import("keytar");
|
|
336
|
-
console.error("[MCP Auth Debug] Keytar imported successfully, getting password...");
|
|
337
|
-
const token = await keytar.getPassword("cutline-mcp", "refresh-token");
|
|
338
|
-
console.error("[MCP Auth Debug] Token retrieved:", token ? "YES (length: " + token.length + ")" : "NO TOKEN FOUND");
|
|
339
|
-
if (token) {
|
|
340
|
-
return { refreshToken: token };
|
|
341
|
-
}
|
|
342
|
-
} catch (error) {
|
|
343
|
-
console.error("[MCP Auth Error] Failed to access keychain:", error);
|
|
344
|
-
}
|
|
345
333
|
return null;
|
|
346
334
|
}
|
|
347
335
|
async function requirePremiumWithAutoAuth(authToken) {
|
|
@@ -904,6 +892,13 @@ async function cfRegenAssumptions(input, doc) {
|
|
|
904
892
|
async function cfRegenExperiments(input, doc) {
|
|
905
893
|
return callCF("regenExperiments", { input, doc }, 9e4);
|
|
906
894
|
}
|
|
895
|
+
async function cfGenerateStructuredContent(options) {
|
|
896
|
+
const res = await proxy("llm.generate", options);
|
|
897
|
+
return res.text;
|
|
898
|
+
}
|
|
899
|
+
async function cfGenerateEmbeddings(texts, taskType = "RETRIEVAL_DOCUMENT") {
|
|
900
|
+
return proxy("embeddings.generate", { texts, taskType });
|
|
901
|
+
}
|
|
907
902
|
async function cfExplorationAgent(message, context) {
|
|
908
903
|
return callCF("explorationAgent", { message, context }, 12e4);
|
|
909
904
|
}
|
|
@@ -1059,6 +1054,8 @@ export {
|
|
|
1059
1054
|
cfPremortemChatAgent,
|
|
1060
1055
|
cfRegenAssumptions,
|
|
1061
1056
|
cfRegenExperiments,
|
|
1057
|
+
cfGenerateStructuredContent,
|
|
1058
|
+
cfGenerateEmbeddings,
|
|
1062
1059
|
cfExplorationAgent,
|
|
1063
1060
|
cfConsultingDiscoveryAgent,
|
|
1064
1061
|
cfCreateLinearIssues,
|
|
@@ -23,7 +23,9 @@ import {
|
|
|
23
23
|
cfCreateLinearIssues,
|
|
24
24
|
cfGenerateAnswer,
|
|
25
25
|
cfGenerateChatSuggestion,
|
|
26
|
+
cfGenerateEmbeddings,
|
|
26
27
|
cfGenerateExplorationResponse,
|
|
28
|
+
cfGenerateStructuredContent,
|
|
27
29
|
cfGenerateTemplateResponse,
|
|
28
30
|
cfGenerateTrialRun,
|
|
29
31
|
cfGetWikiMarkdown,
|
|
@@ -71,7 +73,7 @@ import {
|
|
|
71
73
|
upsertEntities,
|
|
72
74
|
upsertNodes,
|
|
73
75
|
validateRequestSize
|
|
74
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
75
77
|
import {
|
|
76
78
|
GraphTraverser,
|
|
77
79
|
computeGenericGraphMetrics,
|
|
@@ -85,10 +87,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
85
87
|
import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
86
88
|
|
|
87
89
|
// ../mcp/dist/mcp/src/context-graph/embeddings.js
|
|
88
|
-
import { GoogleAuth } from "google-auth-library";
|
|
89
|
-
var EMBEDDING_MODEL = "text-embedding-005";
|
|
90
90
|
var EMBEDDING_DIMENSIONS = 768;
|
|
91
|
-
var DEFAULT_LOCATION = process.env.VERTEX_LOCATION || "us-central1";
|
|
92
91
|
var QUERY_EMBED_CACHE_SIZE = 20;
|
|
93
92
|
var QUERY_EMBED_CACHE_TTL = 30 * 60 * 1e3;
|
|
94
93
|
var queryEmbedCache = /* @__PURE__ */ new Map();
|
|
@@ -113,17 +112,6 @@ function setQueryEmbedCached(query, result) {
|
|
|
113
112
|
}
|
|
114
113
|
queryEmbedCache.set(query, { result, fetchedAt: Date.now() });
|
|
115
114
|
}
|
|
116
|
-
async function resolveProjectId() {
|
|
117
|
-
if (process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT) {
|
|
118
|
-
return process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
|
119
|
-
}
|
|
120
|
-
try {
|
|
121
|
-
const auth = new GoogleAuth();
|
|
122
|
-
return await auth.getProjectId();
|
|
123
|
-
} catch (err) {
|
|
124
|
-
throw new Error(`Unable to resolve Google Cloud project id: ${err?.message ?? err}`);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
115
|
async function generateEmbedding(text) {
|
|
128
116
|
const results = await generateEmbeddings([text]);
|
|
129
117
|
return {
|
|
@@ -133,93 +121,40 @@ async function generateEmbedding(text) {
|
|
|
133
121
|
}
|
|
134
122
|
async function generateEmbeddings(texts) {
|
|
135
123
|
if (texts.length === 0) {
|
|
136
|
-
return { embeddings: [], model:
|
|
124
|
+
return { embeddings: [], model: "text-embedding-005" };
|
|
137
125
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
const batchSize = 5;
|
|
146
|
-
const allEmbeddings = [];
|
|
147
|
-
for (let i = 0; i < texts.length; i += batchSize) {
|
|
148
|
-
const batch = texts.slice(i, i + batchSize);
|
|
149
|
-
const requestBody = {
|
|
150
|
-
instances: batch.map((text) => ({
|
|
151
|
-
content: text,
|
|
152
|
-
task_type: "RETRIEVAL_DOCUMENT"
|
|
153
|
-
// Optimized for retrieval
|
|
154
|
-
}))
|
|
126
|
+
try {
|
|
127
|
+
return await cfGenerateEmbeddings(texts, "RETRIEVAL_DOCUMENT");
|
|
128
|
+
} catch (error) {
|
|
129
|
+
console.error("Embedding proxy error:", error);
|
|
130
|
+
return {
|
|
131
|
+
embeddings: texts.map(() => new Array(EMBEDDING_DIMENSIONS).fill(0)),
|
|
132
|
+
model: "text-embedding-005"
|
|
155
133
|
};
|
|
156
|
-
try {
|
|
157
|
-
const response = await client.request({
|
|
158
|
-
url: endpoint,
|
|
159
|
-
method: "POST",
|
|
160
|
-
data: requestBody
|
|
161
|
-
});
|
|
162
|
-
const data = response.data;
|
|
163
|
-
const predictions = data.predictions || [];
|
|
164
|
-
for (const pred of predictions) {
|
|
165
|
-
const embedding = pred.embeddings?.values || [];
|
|
166
|
-
allEmbeddings.push(embedding);
|
|
167
|
-
}
|
|
168
|
-
} catch (error) {
|
|
169
|
-
console.error("Embedding API error:", error);
|
|
170
|
-
for (let j = 0; j < batch.length; j++) {
|
|
171
|
-
allEmbeddings.push(new Array(EMBEDDING_DIMENSIONS).fill(0));
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
134
|
}
|
|
175
|
-
return {
|
|
176
|
-
embeddings: allEmbeddings,
|
|
177
|
-
model: EMBEDDING_MODEL
|
|
178
|
-
};
|
|
179
135
|
}
|
|
180
136
|
async function generateQueryEmbedding(query) {
|
|
181
137
|
const cached = getQueryEmbedCached(query);
|
|
182
138
|
if (cached)
|
|
183
139
|
return cached;
|
|
184
|
-
|
|
185
|
-
if (result.embedding.some((v) => v !== 0)) {
|
|
186
|
-
setQueryEmbedCached(query, result);
|
|
187
|
-
}
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
async function generateQueryEmbeddingUncached(query) {
|
|
191
|
-
const project = await resolveProjectId();
|
|
192
|
-
const location = DEFAULT_LOCATION;
|
|
193
|
-
const endpoint = `https://${location}-aiplatform.googleapis.com/v1/projects/${project}/locations/${location}/publishers/google/models/${EMBEDDING_MODEL}:predict`;
|
|
194
|
-
const auth = new GoogleAuth({
|
|
195
|
-
scopes: ["https://www.googleapis.com/auth/cloud-platform"]
|
|
196
|
-
});
|
|
197
|
-
const client = await auth.getClient();
|
|
198
|
-
const requestBody = {
|
|
199
|
-
instances: [{
|
|
200
|
-
content: query,
|
|
201
|
-
task_type: "RETRIEVAL_QUERY"
|
|
202
|
-
}]
|
|
203
|
-
};
|
|
140
|
+
let result;
|
|
204
141
|
try {
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
data: requestBody
|
|
209
|
-
});
|
|
210
|
-
const data = response.data;
|
|
211
|
-
const embedding = data.predictions?.[0]?.embeddings?.values || [];
|
|
212
|
-
return {
|
|
213
|
-
embedding,
|
|
142
|
+
const resp = await cfGenerateEmbeddings([query], "RETRIEVAL_QUERY");
|
|
143
|
+
result = {
|
|
144
|
+
embedding: resp.embeddings[0] || new Array(EMBEDDING_DIMENSIONS).fill(0),
|
|
214
145
|
dimensions: EMBEDDING_DIMENSIONS
|
|
215
146
|
};
|
|
216
147
|
} catch (error) {
|
|
217
|
-
console.error("Query embedding error:", error);
|
|
218
|
-
|
|
148
|
+
console.error("Query embedding proxy error:", error);
|
|
149
|
+
result = {
|
|
219
150
|
embedding: new Array(EMBEDDING_DIMENSIONS).fill(0),
|
|
220
151
|
dimensions: EMBEDDING_DIMENSIONS
|
|
221
152
|
};
|
|
222
153
|
}
|
|
154
|
+
if (result.embedding.some((v) => v !== 0)) {
|
|
155
|
+
setQueryEmbedCached(query, result);
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
223
158
|
}
|
|
224
159
|
function cosineSimilarity(a, b) {
|
|
225
160
|
if (a.length !== b.length || a.length === 0)
|
|
@@ -2818,512 +2753,6 @@ async function propagateConstraints(productId, sourceEntityId, targetEntityId, s
|
|
|
2818
2753
|
return propagated;
|
|
2819
2754
|
}
|
|
2820
2755
|
|
|
2821
|
-
// ../mcp/dist/src/orchestrator/agents/shared/vertex.js
|
|
2822
|
-
import { VertexAI } from "@google-cloud/vertexai";
|
|
2823
|
-
import { GoogleAuth as GoogleAuth2 } from "google-auth-library";
|
|
2824
|
-
import { Buffer } from "node:buffer";
|
|
2825
|
-
|
|
2826
|
-
// ../mcp/dist/src/shared/circuit-breaker.js
|
|
2827
|
-
var DEFAULT_OPTIONS = {
|
|
2828
|
-
failureThreshold: 5,
|
|
2829
|
-
resetTimeoutMs: 6e4,
|
|
2830
|
-
halfOpenMaxAttempts: 1,
|
|
2831
|
-
name: "default"
|
|
2832
|
-
};
|
|
2833
|
-
var CircuitBreakerOpenError = class extends Error {
|
|
2834
|
-
circuitName;
|
|
2835
|
-
retriesAt;
|
|
2836
|
-
constructor(circuitName, retriesAt) {
|
|
2837
|
-
super(`Circuit "${circuitName}" is open \u2014 fast-failing. Retries at ${new Date(retriesAt).toISOString()}`);
|
|
2838
|
-
this.circuitName = circuitName;
|
|
2839
|
-
this.retriesAt = retriesAt;
|
|
2840
|
-
this.name = "CircuitBreakerOpenError";
|
|
2841
|
-
}
|
|
2842
|
-
};
|
|
2843
|
-
var CircuitBreaker = class {
|
|
2844
|
-
state = "closed";
|
|
2845
|
-
failureCount = 0;
|
|
2846
|
-
lastFailureTime = 0;
|
|
2847
|
-
halfOpenAttempts = 0;
|
|
2848
|
-
opts;
|
|
2849
|
-
constructor(options) {
|
|
2850
|
-
this.opts = { ...DEFAULT_OPTIONS, ...options };
|
|
2851
|
-
}
|
|
2852
|
-
getState() {
|
|
2853
|
-
this.maybeTransition();
|
|
2854
|
-
return this.state;
|
|
2855
|
-
}
|
|
2856
|
-
getStats() {
|
|
2857
|
-
return { state: this.getState(), failureCount: this.failureCount, lastFailureTime: this.lastFailureTime };
|
|
2858
|
-
}
|
|
2859
|
-
/**
|
|
2860
|
-
* Execute a function through the circuit breaker.
|
|
2861
|
-
* Throws CircuitBreakerOpenError if the circuit is open.
|
|
2862
|
-
*/
|
|
2863
|
-
async execute(fn) {
|
|
2864
|
-
this.maybeTransition();
|
|
2865
|
-
if (this.state === "open") {
|
|
2866
|
-
throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
|
|
2867
|
-
}
|
|
2868
|
-
if (this.state === "half_open" && this.halfOpenAttempts >= this.opts.halfOpenMaxAttempts) {
|
|
2869
|
-
throw new CircuitBreakerOpenError(this.opts.name, this.lastFailureTime + this.opts.resetTimeoutMs);
|
|
2870
|
-
}
|
|
2871
|
-
if (this.state === "half_open") {
|
|
2872
|
-
this.halfOpenAttempts++;
|
|
2873
|
-
}
|
|
2874
|
-
try {
|
|
2875
|
-
const result = await fn();
|
|
2876
|
-
this.onSuccess();
|
|
2877
|
-
return result;
|
|
2878
|
-
} catch (err) {
|
|
2879
|
-
this.onFailure();
|
|
2880
|
-
throw err;
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
/** Force the circuit into a specific state (useful for testing) */
|
|
2884
|
-
reset() {
|
|
2885
|
-
this.state = "closed";
|
|
2886
|
-
this.failureCount = 0;
|
|
2887
|
-
this.halfOpenAttempts = 0;
|
|
2888
|
-
this.lastFailureTime = 0;
|
|
2889
|
-
}
|
|
2890
|
-
onSuccess() {
|
|
2891
|
-
this.failureCount = 0;
|
|
2892
|
-
this.halfOpenAttempts = 0;
|
|
2893
|
-
if (this.state === "half_open") {
|
|
2894
|
-
this.state = "closed";
|
|
2895
|
-
console.log(JSON.stringify({
|
|
2896
|
-
severity: "INFO",
|
|
2897
|
-
circuit_breaker: this.opts.name,
|
|
2898
|
-
event: "circuit_closed",
|
|
2899
|
-
message: `Circuit "${this.opts.name}" recovered \u2014 closing`
|
|
2900
|
-
}));
|
|
2901
|
-
}
|
|
2902
|
-
}
|
|
2903
|
-
onFailure() {
|
|
2904
|
-
this.failureCount++;
|
|
2905
|
-
this.lastFailureTime = Date.now();
|
|
2906
|
-
if (this.state === "half_open") {
|
|
2907
|
-
this.state = "open";
|
|
2908
|
-
console.log(JSON.stringify({
|
|
2909
|
-
severity: "WARNING",
|
|
2910
|
-
circuit_breaker: this.opts.name,
|
|
2911
|
-
event: "circuit_reopened",
|
|
2912
|
-
message: `Circuit "${this.opts.name}" probe failed \u2014 re-opening`
|
|
2913
|
-
}));
|
|
2914
|
-
return;
|
|
2915
|
-
}
|
|
2916
|
-
if (this.failureCount >= this.opts.failureThreshold) {
|
|
2917
|
-
this.state = "open";
|
|
2918
|
-
console.log(JSON.stringify({
|
|
2919
|
-
severity: "WARNING",
|
|
2920
|
-
circuit_breaker: this.opts.name,
|
|
2921
|
-
event: "circuit_opened",
|
|
2922
|
-
failure_count: this.failureCount,
|
|
2923
|
-
message: `Circuit "${this.opts.name}" opened after ${this.failureCount} consecutive failures`
|
|
2924
|
-
}));
|
|
2925
|
-
}
|
|
2926
|
-
}
|
|
2927
|
-
maybeTransition() {
|
|
2928
|
-
if (this.state === "open") {
|
|
2929
|
-
const elapsed = Date.now() - this.lastFailureTime;
|
|
2930
|
-
if (elapsed >= this.opts.resetTimeoutMs) {
|
|
2931
|
-
this.state = "half_open";
|
|
2932
|
-
this.halfOpenAttempts = 0;
|
|
2933
|
-
console.log(JSON.stringify({
|
|
2934
|
-
severity: "INFO",
|
|
2935
|
-
circuit_breaker: this.opts.name,
|
|
2936
|
-
event: "circuit_half_open",
|
|
2937
|
-
message: `Circuit "${this.opts.name}" entering half-open after ${Math.round(elapsed / 1e3)}s`
|
|
2938
|
-
}));
|
|
2939
|
-
}
|
|
2940
|
-
}
|
|
2941
|
-
}
|
|
2942
|
-
};
|
|
2943
|
-
|
|
2944
|
-
// ../mcp/dist/src/shared/metrics.js
|
|
2945
|
-
function emitMetric(name2, value, labels) {
|
|
2946
|
-
const record = {
|
|
2947
|
-
metric: true,
|
|
2948
|
-
severity: "INFO",
|
|
2949
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2950
|
-
name: name2,
|
|
2951
|
-
value,
|
|
2952
|
-
...labels && { labels: stripUndefined(labels) }
|
|
2953
|
-
};
|
|
2954
|
-
console.log(JSON.stringify(record));
|
|
2955
|
-
}
|
|
2956
|
-
function stripUndefined(obj) {
|
|
2957
|
-
const out = {};
|
|
2958
|
-
for (const [k, v] of Object.entries(obj)) {
|
|
2959
|
-
if (v !== void 0)
|
|
2960
|
-
out[k] = v;
|
|
2961
|
-
}
|
|
2962
|
-
return out;
|
|
2963
|
-
}
|
|
2964
|
-
|
|
2965
|
-
// ../mcp/dist/src/orchestrator/agents/shared/vertex.js
|
|
2966
|
-
var MODEL_PRICING = {
|
|
2967
|
-
"gemini-2.5-pro": { inputPerM: 1.25, outputPerM: 10 },
|
|
2968
|
-
"gemini-2.5-flash": { inputPerM: 0.15, outputPerM: 0.6 },
|
|
2969
|
-
"gemini-2.0-flash": { inputPerM: 0.1, outputPerM: 0.4 },
|
|
2970
|
-
"gemini-1.5-pro": { inputPerM: 1.25, outputPerM: 5 },
|
|
2971
|
-
"gemini-1.5-flash": { inputPerM: 0.075, outputPerM: 0.3 }
|
|
2972
|
-
};
|
|
2973
|
-
function estimateCostUsd(model, tokensIn, tokensOut) {
|
|
2974
|
-
const key = Object.keys(MODEL_PRICING).find((k) => model.startsWith(k)) ?? "";
|
|
2975
|
-
const price = MODEL_PRICING[key];
|
|
2976
|
-
if (!price)
|
|
2977
|
-
return 0;
|
|
2978
|
-
return Math.round((tokensIn * price.inputPerM + tokensOut * price.outputPerM) / 1e6 * 1e6) / 1e6;
|
|
2979
|
-
}
|
|
2980
|
-
var DEFAULT_MODEL_ID = process.env.MODEL_ID || "gemini-2.5-pro";
|
|
2981
|
-
var DEFAULT_LOCATION2 = process.env.VERTEX_LOCATION || "us-central1";
|
|
2982
|
-
var VERBOSE = (process.env.AGENT_LOG_VERBOSE ?? process.env.PREMORTEM_LOG_VERBOSE) === "true";
|
|
2983
|
-
var PREVIEW = process.env.AGENT_LOG_PREVIEW === "true";
|
|
2984
|
-
var PREVIEW_LIMIT = Math.max(0, parseInt(process.env.AGENT_LOG_PREVIEW_LIMIT || "400", 10) || 400);
|
|
2985
|
-
function vlog(...args) {
|
|
2986
|
-
if (VERBOSE) {
|
|
2987
|
-
try {
|
|
2988
|
-
console.log(...args);
|
|
2989
|
-
} catch {
|
|
2990
|
-
}
|
|
2991
|
-
}
|
|
2992
|
-
}
|
|
2993
|
-
function safeStringify(x) {
|
|
2994
|
-
try {
|
|
2995
|
-
return JSON.stringify(x);
|
|
2996
|
-
} catch {
|
|
2997
|
-
return String(x);
|
|
2998
|
-
}
|
|
2999
|
-
}
|
|
3000
|
-
function previewOf(x) {
|
|
3001
|
-
const s = typeof x === "string" ? x : safeStringify(x);
|
|
3002
|
-
return s.length > PREVIEW_LIMIT ? s.slice(0, PREVIEW_LIMIT) + "\u2026" : s;
|
|
3003
|
-
}
|
|
3004
|
-
var vertexCircuit = new CircuitBreaker({
|
|
3005
|
-
name: "vertex-ai",
|
|
3006
|
-
failureThreshold: 5,
|
|
3007
|
-
resetTimeoutMs: 6e4,
|
|
3008
|
-
halfOpenMaxAttempts: 1
|
|
3009
|
-
});
|
|
3010
|
-
var LAST_DIAG = null;
|
|
3011
|
-
function asVertexResponseSchema(schema) {
|
|
3012
|
-
const convert = (s) => {
|
|
3013
|
-
const out = {};
|
|
3014
|
-
if (s.type)
|
|
3015
|
-
out.type = String(s.type).toLowerCase();
|
|
3016
|
-
if (s.description != null)
|
|
3017
|
-
out.description = s.description;
|
|
3018
|
-
if (Array.isArray(s.enum))
|
|
3019
|
-
out.enum = s.enum;
|
|
3020
|
-
if (s.items)
|
|
3021
|
-
out.items = convert(s.items);
|
|
3022
|
-
if (s.properties) {
|
|
3023
|
-
out.properties = {};
|
|
3024
|
-
for (const [k, v] of Object.entries(s.properties)) {
|
|
3025
|
-
out.properties[k] = convert(v);
|
|
3026
|
-
}
|
|
3027
|
-
}
|
|
3028
|
-
if (Array.isArray(s.required))
|
|
3029
|
-
out.required = s.required;
|
|
3030
|
-
if (s.minItems != null)
|
|
3031
|
-
out.minItems = s.minItems;
|
|
3032
|
-
if (s.maxItems != null)
|
|
3033
|
-
out.maxItems = s.maxItems;
|
|
3034
|
-
if (s.minimum != null)
|
|
3035
|
-
out.minimum = s.minimum;
|
|
3036
|
-
if (s.maximum != null)
|
|
3037
|
-
out.maximum = s.maximum;
|
|
3038
|
-
if (s.minLength != null)
|
|
3039
|
-
out.minLength = s.minLength;
|
|
3040
|
-
if (s.maxLength != null)
|
|
3041
|
-
out.maxLength = s.maxLength;
|
|
3042
|
-
if (s.pattern != null)
|
|
3043
|
-
out.pattern = s.pattern;
|
|
3044
|
-
if (s.nullable != null)
|
|
3045
|
-
out.nullable = s.nullable;
|
|
3046
|
-
return out;
|
|
3047
|
-
};
|
|
3048
|
-
return convert(schema);
|
|
3049
|
-
}
|
|
3050
|
-
async function retryWithBackoff(fn, opts) {
|
|
3051
|
-
let lastErr;
|
|
3052
|
-
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
3053
|
-
try {
|
|
3054
|
-
return await fn();
|
|
3055
|
-
} catch (err) {
|
|
3056
|
-
lastErr = err;
|
|
3057
|
-
if (attempt >= opts.maxRetries || !opts.shouldRetry(err))
|
|
3058
|
-
throw err;
|
|
3059
|
-
const delayMs = opts.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
|
|
3060
|
-
vlog("vertex_retry", { attempt: attempt + 1, delayMs: Math.round(delayMs), error: String(err?.message || err).slice(0, 200) });
|
|
3061
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
3062
|
-
}
|
|
3063
|
-
}
|
|
3064
|
-
throw lastErr;
|
|
3065
|
-
}
|
|
3066
|
-
async function generateStructuredContent(options) {
|
|
3067
|
-
const project = await resolveProjectId2();
|
|
3068
|
-
const modelId = options.modelId || DEFAULT_MODEL_ID;
|
|
3069
|
-
const location = options.location || DEFAULT_LOCATION2;
|
|
3070
|
-
const vertex = new VertexAI({ project, location });
|
|
3071
|
-
const fullSystem = `${GLOBAL_SYSTEM_PROMPT}
|
|
3072
|
-
|
|
3073
|
-
${options.system || ""}`.trim();
|
|
3074
|
-
const model = vertex.getGenerativeModel({
|
|
3075
|
-
model: modelId,
|
|
3076
|
-
systemInstruction: { role: "system", parts: [{ text: fullSystem }] }
|
|
3077
|
-
});
|
|
3078
|
-
const t0 = Date.now();
|
|
3079
|
-
if (PREVIEW) {
|
|
3080
|
-
try {
|
|
3081
|
-
vlog("vertex_prompt", { model: modelId, location, system_preview: previewOf(fullSystem), user_preview: previewOf(options.user) });
|
|
3082
|
-
} catch {
|
|
3083
|
-
}
|
|
3084
|
-
}
|
|
3085
|
-
const tools = options.useGoogleSearch ? [{
|
|
3086
|
-
googleSearch: {}
|
|
3087
|
-
}] : void 0;
|
|
3088
|
-
const useStructuredOutput = options.responseSchema && !options.useGoogleSearch;
|
|
3089
|
-
const requestPayload = {
|
|
3090
|
-
contents: [{ role: "user", parts: [{ text: options.user }] }],
|
|
3091
|
-
generationConfig: {
|
|
3092
|
-
temperature: options.temperature ?? 0.2,
|
|
3093
|
-
maxOutputTokens: options.maxOutputTokens ?? 400,
|
|
3094
|
-
responseMimeType: useStructuredOutput ? "application/json" : void 0,
|
|
3095
|
-
responseSchema: useStructuredOutput ? asVertexResponseSchema(options.responseSchema) : void 0,
|
|
3096
|
-
topP: options.topP ?? 0.9,
|
|
3097
|
-
topK: options.topK ?? 40
|
|
3098
|
-
},
|
|
3099
|
-
tools
|
|
3100
|
-
};
|
|
3101
|
-
const response = await vertexCircuit.execute(() => retryWithBackoff(() => model.generateContent(requestPayload), {
|
|
3102
|
-
maxRetries: 3,
|
|
3103
|
-
baseDelayMs: 2e3,
|
|
3104
|
-
shouldRetry: (err) => {
|
|
3105
|
-
const msg = String(err?.message || err || "");
|
|
3106
|
-
const name2 = String(err?.name || "");
|
|
3107
|
-
if (msg.includes("429") || msg.includes("RESOURCE_EXHAUSTED"))
|
|
3108
|
-
return true;
|
|
3109
|
-
if (msg.includes("503") || msg.includes("500") || msg.includes("UNAVAILABLE"))
|
|
3110
|
-
return true;
|
|
3111
|
-
if (msg.includes("exception posting request") || msg.includes("ECONNRESET") || msg.includes("ETIMEDOUT") || msg.includes("socket hang up") || msg.includes("network") || msg.includes("fetch failed"))
|
|
3112
|
-
return true;
|
|
3113
|
-
if (name2 === "GoogleGenerativeAIError" || name2 === "GoogleGenerativeAIFetchError")
|
|
3114
|
-
return true;
|
|
3115
|
-
return false;
|
|
3116
|
-
}
|
|
3117
|
-
}));
|
|
3118
|
-
const candidate = response.response.candidates?.[0];
|
|
3119
|
-
const parts = candidate?.content?.parts ?? [];
|
|
3120
|
-
const finishReason = candidate?.finishReason ?? candidate?.finish_reason;
|
|
3121
|
-
const usage = response?.response?.usageMetadata ?? response?.usageMetadata;
|
|
3122
|
-
const safety = candidate?.safetyRatings ?? candidate?.safety_ratings;
|
|
3123
|
-
const partTypes = parts.map((p) => p?.functionCall ? "functionCall" : p?.structValue ? "structValue" : p?.inlineData ? "inlineData" : typeof p?.text === "string" ? "text" : "other");
|
|
3124
|
-
const groundingMeta = candidate?.groundingMetadata;
|
|
3125
|
-
const groundingMetadata = groundingMeta ? {
|
|
3126
|
-
searchQueries: groundingMeta.searchEntryPoint?.renderedContent ? [groundingMeta.searchEntryPoint.renderedContent] : void 0,
|
|
3127
|
-
webSearchQueries: groundingMeta.webSearchQueries,
|
|
3128
|
-
groundingChunks: groundingMeta.groundingChunks?.length
|
|
3129
|
-
} : void 0;
|
|
3130
|
-
if (groundingMetadata?.groundingChunks) {
|
|
3131
|
-
vlog("vertex_grounding", {
|
|
3132
|
-
searchQueries: groundingMetadata.webSearchQueries,
|
|
3133
|
-
chunks: groundingMetadata.groundingChunks
|
|
3134
|
-
});
|
|
3135
|
-
}
|
|
3136
|
-
const pickNum = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
3137
|
-
const tokens_in = pickNum(usage?.promptTokenCount ?? usage?.inputTokenCount ?? usage?.promptTokens ?? usage?.input_tokens);
|
|
3138
|
-
const tokens_out = pickNum(usage?.candidatesTokenCount ?? usage?.outputTokenCount ?? usage?.completionTokens ?? usage?.output_tokens);
|
|
3139
|
-
const total_tokens = pickNum(usage?.totalTokenCount ?? usage?.totalTokens) ?? (tokens_in ?? 0) + (tokens_out ?? 0);
|
|
3140
|
-
const cost_usd = estimateCostUsd(modelId, tokens_in ?? 0, tokens_out ?? 0);
|
|
3141
|
-
emitMetric("vertex.call.tokens", total_tokens ?? 0, {
|
|
3142
|
-
model: modelId,
|
|
3143
|
-
tokens_in: tokens_in ?? 0,
|
|
3144
|
-
tokens_out: tokens_out ?? 0,
|
|
3145
|
-
cost_usd,
|
|
3146
|
-
latency_ms: Date.now() - t0
|
|
3147
|
-
});
|
|
3148
|
-
const logVertex = (selected, lengths) => {
|
|
3149
|
-
if (!VERBOSE)
|
|
3150
|
-
return;
|
|
3151
|
-
vlog("vertex_done", { model: modelId, location, tokens_in, tokens_out, total_tokens, finishReason, selected, parts_count: partTypes.length, latency_ms: Date.now() - t0, lengths });
|
|
3152
|
-
};
|
|
3153
|
-
if (!parts.length) {
|
|
3154
|
-
try {
|
|
3155
|
-
const out = JSON.stringify(response.response);
|
|
3156
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: out.length }, groundingMetadata };
|
|
3157
|
-
logVertex("text", { funcArgs: 0, inline: 0, text: out.length });
|
|
3158
|
-
return out;
|
|
3159
|
-
} catch {
|
|
3160
|
-
}
|
|
3161
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: 0, types: [] }, selected: "text", lengths: { funcArgs: 0, inline: 0, text: 0 }, groundingMetadata };
|
|
3162
|
-
logVertex("text", { funcArgs: 0, inline: 0, text: 0 });
|
|
3163
|
-
return "";
|
|
3164
|
-
}
|
|
3165
|
-
const textSegments = [];
|
|
3166
|
-
let funcArgs = "";
|
|
3167
|
-
let sawFuncArgs = false;
|
|
3168
|
-
let structCandidate = void 0;
|
|
3169
|
-
const inlineSegments = [];
|
|
3170
|
-
const preferText = !!options.preferText && !options.responseSchema;
|
|
3171
|
-
for (const p of parts) {
|
|
3172
|
-
if (p?.functionCall?.arguments !== void 0) {
|
|
3173
|
-
const args = p.functionCall.arguments;
|
|
3174
|
-
sawFuncArgs = true;
|
|
3175
|
-
if (typeof args === "string") {
|
|
3176
|
-
funcArgs += args;
|
|
3177
|
-
} else {
|
|
3178
|
-
try {
|
|
3179
|
-
structCandidate = args;
|
|
3180
|
-
} catch {
|
|
3181
|
-
}
|
|
3182
|
-
}
|
|
3183
|
-
continue;
|
|
3184
|
-
}
|
|
3185
|
-
if (p?.structValue) {
|
|
3186
|
-
structCandidate = p.structValue;
|
|
3187
|
-
continue;
|
|
3188
|
-
}
|
|
3189
|
-
if (p?.inlineData?.data) {
|
|
3190
|
-
try {
|
|
3191
|
-
const decoded = Buffer.from(p.inlineData.data, "base64").toString("utf8");
|
|
3192
|
-
inlineSegments.push(decoded);
|
|
3193
|
-
} catch {
|
|
3194
|
-
}
|
|
3195
|
-
continue;
|
|
3196
|
-
}
|
|
3197
|
-
if (typeof p?.text === "string") {
|
|
3198
|
-
textSegments.push(p.text);
|
|
3199
|
-
}
|
|
3200
|
-
}
|
|
3201
|
-
if (!preferText && sawFuncArgs && funcArgs.trim().length) {
|
|
3202
|
-
let out = funcArgs.trim();
|
|
3203
|
-
if (out.startsWith("```")) {
|
|
3204
|
-
out = out.replace(/^```[a-zA-Z]*\n?/, "");
|
|
3205
|
-
if (out.endsWith("```"))
|
|
3206
|
-
out = out.replace(/```\s*$/, "");
|
|
3207
|
-
}
|
|
3208
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "args", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length }, groundingMetadata };
|
|
3209
|
-
logVertex("args", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
|
|
3210
|
-
return out;
|
|
3211
|
-
}
|
|
3212
|
-
if (!preferText && structCandidate !== void 0) {
|
|
3213
|
-
try {
|
|
3214
|
-
const out = JSON.stringify(structCandidate);
|
|
3215
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "struct", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length }, groundingMetadata };
|
|
3216
|
-
logVertex("struct", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: textSegments.join("").length });
|
|
3217
|
-
return out;
|
|
3218
|
-
} catch {
|
|
3219
|
-
}
|
|
3220
|
-
}
|
|
3221
|
-
if (inlineSegments.length) {
|
|
3222
|
-
let inlineText = inlineSegments.join("").trim();
|
|
3223
|
-
if (inlineText.startsWith("```")) {
|
|
3224
|
-
inlineText = inlineText.replace(/^```[a-zA-Z]*\n?/, "");
|
|
3225
|
-
if (inlineText.endsWith("```"))
|
|
3226
|
-
inlineText = inlineText.replace(/```\s*$/, "");
|
|
3227
|
-
}
|
|
3228
|
-
if (options.responseSchema) {
|
|
3229
|
-
try {
|
|
3230
|
-
JSON.parse(inlineText);
|
|
3231
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "inline", lengths: { funcArgs: funcArgs.length, inline: inlineText.length, text: textSegments.join("").length }, groundingMetadata };
|
|
3232
|
-
logVertex("inline", { funcArgs: funcArgs.length, inline: inlineText.length, text: textSegments.join("").length });
|
|
3233
|
-
return inlineText;
|
|
3234
|
-
} catch {
|
|
3235
|
-
}
|
|
3236
|
-
const start = inlineText.indexOf("{");
|
|
3237
|
-
const end = inlineText.lastIndexOf("}");
|
|
3238
|
-
if (start !== -1 && end > start) {
|
|
3239
|
-
const inner = inlineText.slice(start, end + 1).trim();
|
|
3240
|
-
try {
|
|
3241
|
-
JSON.parse(inner);
|
|
3242
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "inline", lengths: { funcArgs: funcArgs.length, inline: inner.length, text: textSegments.join("").length }, groundingMetadata };
|
|
3243
|
-
logVertex("inline", { funcArgs: funcArgs.length, inline: inner.length, text: textSegments.join("").length });
|
|
3244
|
-
return inner;
|
|
3245
|
-
} catch {
|
|
3246
|
-
}
|
|
3247
|
-
}
|
|
3248
|
-
}
|
|
3249
|
-
if (inlineText) {
|
|
3250
|
-
textSegments.push(inlineText);
|
|
3251
|
-
}
|
|
3252
|
-
}
|
|
3253
|
-
let text = textSegments.join("").trim();
|
|
3254
|
-
if (!text) {
|
|
3255
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 }, groundingMetadata };
|
|
3256
|
-
logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: 0 });
|
|
3257
|
-
return "";
|
|
3258
|
-
}
|
|
3259
|
-
if (text.startsWith("```")) {
|
|
3260
|
-
text = text.replace(/^```[a-zA-Z]*\n?/, "");
|
|
3261
|
-
if (text.endsWith("```"))
|
|
3262
|
-
text = text.replace(/```\s*$/, "");
|
|
3263
|
-
}
|
|
3264
|
-
if (options.responseSchema) {
|
|
3265
|
-
try {
|
|
3266
|
-
JSON.parse(text);
|
|
3267
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length }, groundingMetadata };
|
|
3268
|
-
logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
|
|
3269
|
-
return text;
|
|
3270
|
-
} catch {
|
|
3271
|
-
}
|
|
3272
|
-
const start = text.indexOf("{");
|
|
3273
|
-
const end = text.lastIndexOf("}");
|
|
3274
|
-
if (start !== -1 && end > start) {
|
|
3275
|
-
const inner = text.slice(start, end + 1).trim();
|
|
3276
|
-
try {
|
|
3277
|
-
JSON.parse(inner);
|
|
3278
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: inner.length }, groundingMetadata };
|
|
3279
|
-
logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: inner.length });
|
|
3280
|
-
return inner;
|
|
3281
|
-
} catch {
|
|
3282
|
-
}
|
|
3283
|
-
}
|
|
3284
|
-
}
|
|
3285
|
-
LAST_DIAG = { finishReason, usage, safety, parts: { count: partTypes.length, types: partTypes }, selected: "text", lengths: { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length }, groundingMetadata };
|
|
3286
|
-
logVertex("text", { funcArgs: funcArgs.length, inline: inlineSegments.join("").length, text: text.length });
|
|
3287
|
-
return text;
|
|
3288
|
-
}
|
|
3289
|
-
var GLOBAL_SYSTEM_PROMPT = [
|
|
3290
|
-
"You are a research-first, skepticism-driven product analyst. Your job is to reduce optimism bias and avoid guesswork.",
|
|
3291
|
-
"",
|
|
3292
|
-
"Behavioral guardrails:",
|
|
3293
|
-
"- Evidence > opinions. Prefer verifiable facts, base rates, and conservative assumptions.",
|
|
3294
|
-
"- Do not fabricate specifics (data points, sources, user counts, dates). If unknown, say so.",
|
|
3295
|
-
"- Calibrate: reflect uncertainty explicitly and note assumptions driving your outputs.",
|
|
3296
|
-
"- Prefer breadth from reference classes over narrow anecdotes. De-duplicate aggressively.",
|
|
3297
|
-
"",
|
|
3298
|
-
"When context is insufficient:",
|
|
3299
|
-
"- Identify Information Gaps (what is missing to answer confidently).",
|
|
3300
|
-
"- Propose How to Research with 3\u20136 concrete queries and credible source types to check.",
|
|
3301
|
-
"- Use analogous reference classes to ground estimates, but flag them as assumptions.",
|
|
3302
|
-
"",
|
|
3303
|
-
"Estimation policy:",
|
|
3304
|
-
"- For any numeric estimate required by the schema, return a realistic P50 value.",
|
|
3305
|
-
"- In rationale, include a short range and the main driver(s). Default conservative when uncertainty is high.",
|
|
3306
|
-
"",
|
|
3307
|
-
"MoSCoW policy:",
|
|
3308
|
-
"- MUST only if critical to core value or risk mitigation; SHOULD for strong value; COULD if deferrable; WONT if low value now or blocked (say why).",
|
|
3309
|
-
"",
|
|
3310
|
-
"Output hygiene:",
|
|
3311
|
-
"- Follow the requested output schema exactly. If a field is unknown, omit it or use null if allowed.",
|
|
3312
|
-
"- Keep rationales crisp (8\u201330 words), focusing on evidence, base rates, and key assumptions.",
|
|
3313
|
-
"- No marketing language, no boilerplate, no code fences, no markdown\u2014only the required JSON/plain text."
|
|
3314
|
-
].join("\n");
|
|
3315
|
-
async function resolveProjectId2() {
|
|
3316
|
-
if (process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT) {
|
|
3317
|
-
return process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
|
3318
|
-
}
|
|
3319
|
-
try {
|
|
3320
|
-
const auth = new GoogleAuth2();
|
|
3321
|
-
return await auth.getProjectId();
|
|
3322
|
-
} catch (err) {
|
|
3323
|
-
throw new Error(`Unable to resolve Google Cloud project id: ${err?.message ?? err}`);
|
|
3324
|
-
}
|
|
3325
|
-
}
|
|
3326
|
-
|
|
3327
2756
|
// ../mcp/dist/mcp/src/constraints/constraint-matcher.js
|
|
3328
2757
|
var MATCH_RULES = [
|
|
3329
2758
|
{
|
|
@@ -7763,7 +7192,7 @@ function mcpAudit(entry) {
|
|
|
7763
7192
|
}));
|
|
7764
7193
|
}
|
|
7765
7194
|
var DEFAULT_MODEL = process.env.MODEL_ID || "gemini-2.5-pro";
|
|
7766
|
-
var
|
|
7195
|
+
var generateStructuredContent = (options) => withLlmMonitor(options.modelId || DEFAULT_MODEL, () => cfGenerateStructuredContent(options));
|
|
7767
7196
|
var explorationSessions = /* @__PURE__ */ new Map();
|
|
7768
7197
|
function generateId(prefix = "id") {
|
|
7769
7198
|
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -8813,7 +8242,7 @@ Why AI: ${idea.whyAI}`
|
|
|
8813
8242
|
getAllEdges: (pid) => getAllEdges(pid),
|
|
8814
8243
|
getAllNodes: (pid) => getAllNodes(pid),
|
|
8815
8244
|
getAllBindings: (pid) => getAllBindings(pid),
|
|
8816
|
-
generateStructuredContent
|
|
8245
|
+
generateStructuredContent,
|
|
8817
8246
|
upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
|
|
8818
8247
|
addEntity: (pid, entity) => addEntity(pid, entity),
|
|
8819
8248
|
addEdges: (pid, edges) => addEdges(pid, edges),
|
|
@@ -9231,7 +8660,7 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
9231
8660
|
throw new McpError(ErrorCode.InvalidParams, "Document text too short (min 50 chars)");
|
|
9232
8661
|
}
|
|
9233
8662
|
const finalDocId = doc_id || `doc_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
9234
|
-
const nodes = await extractConstraintsFromDoc(finalDocId, text,
|
|
8663
|
+
const nodes = await extractConstraintsFromDoc(finalDocId, text, generateStructuredContent, { documentTitle: title, documentUrl: url });
|
|
9235
8664
|
if (nodes.length === 0) {
|
|
9236
8665
|
return {
|
|
9237
8666
|
content: [{
|
|
@@ -9968,7 +9397,7 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
9968
9397
|
}
|
|
9969
9398
|
const ingestionId = source_id || `req_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
9970
9399
|
const existingEntities = await getAllEntities(product_id);
|
|
9971
|
-
const graph = await ingestRequirements(ingestionId, text,
|
|
9400
|
+
const graph = await ingestRequirements(ingestionId, text, generateStructuredContent, existingEntities, { documentTitle: title });
|
|
9972
9401
|
if (graph.entities.length > 0) {
|
|
9973
9402
|
await upsertEntities(product_id, "doc", ingestionId, graph.entities);
|
|
9974
9403
|
}
|
|
@@ -10124,7 +9553,7 @@ Meta: ${JSON.stringify(meta)}`
|
|
|
10124
9553
|
}]
|
|
10125
9554
|
};
|
|
10126
9555
|
}
|
|
10127
|
-
const result = await inferBindings(entities, file_paths, use_llm ?
|
|
9556
|
+
const result = await inferBindings(entities, file_paths, use_llm ? generateStructuredContent : void 0);
|
|
10128
9557
|
if (result.bindings.length > 0) {
|
|
10129
9558
|
await upsertBindings(product_id, result.bindings);
|
|
10130
9559
|
}
|
|
@@ -10798,7 +10227,7 @@ Meta: ${JSON.stringify({
|
|
|
10798
10227
|
getAllEdges: (pid) => getAllEdges(pid),
|
|
10799
10228
|
getAllNodes: (pid) => getAllNodes(pid),
|
|
10800
10229
|
getAllBindings: (pid) => getAllBindings(pid),
|
|
10801
|
-
generateStructuredContent
|
|
10230
|
+
generateStructuredContent,
|
|
10802
10231
|
upsertNodes: (pid, sourceType, sourceId, nodes) => upsertNodes(pid, sourceType, sourceId, nodes),
|
|
10803
10232
|
addEntity: (pid, entity) => addEntity(pid, entity),
|
|
10804
10233
|
addEdges: (pid, edges) => addEdges(pid, edges),
|
|
@@ -11,7 +11,9 @@ import {
|
|
|
11
11
|
cfExplorationAgent,
|
|
12
12
|
cfGenerateAnswer,
|
|
13
13
|
cfGenerateChatSuggestion,
|
|
14
|
+
cfGenerateEmbeddings,
|
|
14
15
|
cfGenerateExplorationResponse,
|
|
16
|
+
cfGenerateStructuredContent,
|
|
15
17
|
cfGenerateTemplateResponse,
|
|
16
18
|
cfGenerateTrialRun,
|
|
17
19
|
cfGetWikiMarkdown,
|
|
@@ -75,7 +77,7 @@ import {
|
|
|
75
77
|
upsertEdges,
|
|
76
78
|
upsertEntities,
|
|
77
79
|
upsertNodes
|
|
78
|
-
} from "./chunk-
|
|
80
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
79
81
|
export {
|
|
80
82
|
addEdges,
|
|
81
83
|
addEntity,
|
|
@@ -89,7 +91,9 @@ export {
|
|
|
89
91
|
cfExplorationAgent,
|
|
90
92
|
cfGenerateAnswer,
|
|
91
93
|
cfGenerateChatSuggestion,
|
|
94
|
+
cfGenerateEmbeddings,
|
|
92
95
|
cfGenerateExplorationResponse,
|
|
96
|
+
cfGenerateStructuredContent,
|
|
93
97
|
cfGenerateTemplateResponse,
|
|
94
98
|
cfGenerateTrialRun,
|
|
95
99
|
cfGetWikiMarkdown,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
requirePremiumWithAutoAuth,
|
|
15
15
|
updateExplorationSession,
|
|
16
16
|
validateRequestSize
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
18
18
|
|
|
19
19
|
// ../mcp/dist/mcp/src/exploration-server.js
|
|
20
20
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
requirePremiumWithAutoAuth,
|
|
14
14
|
validateAuth,
|
|
15
15
|
validateRequestSize
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
17
17
|
|
|
18
18
|
// ../mcp/dist/mcp/src/integrations-server.js
|
|
19
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
mapErrorToMcp,
|
|
14
14
|
requirePremiumWithAutoAuth,
|
|
15
15
|
validateRequestSize
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
17
17
|
|
|
18
18
|
// ../mcp/dist/mcp/src/output-server.js
|
|
19
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
requirePremiumWithAutoAuth,
|
|
22
22
|
validateAuth,
|
|
23
23
|
validateRequestSize
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-ZVWDXO6M.js";
|
|
25
25
|
|
|
26
26
|
// ../mcp/dist/mcp/src/tools-server.js
|
|
27
27
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kylewadegrove/cutline-mcp-cli",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"description": "CLI and MCP servers for Cutline — authenticate, then run constraint-aware MCP servers in Cursor or any MCP client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -53,13 +53,10 @@
|
|
|
53
53
|
"author": "Cutline",
|
|
54
54
|
"license": "MIT",
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@google-cloud/vertexai": "^1.9.2",
|
|
57
56
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
58
57
|
"chalk": "^4.1.2",
|
|
59
58
|
"commander": "^11.1.0",
|
|
60
59
|
"dotenv": "^16.4.5",
|
|
61
|
-
"google-auth-library": "^9.14.2",
|
|
62
|
-
"keytar": "^7.9.0",
|
|
63
60
|
"open": "^9.1.0",
|
|
64
61
|
"ora": "^5.4.1",
|
|
65
62
|
"zod": "^3.23.8"
|