thumbgate 1.27.17 → 1.27.18
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/.claude-plugin/marketplace.json +85 -0
- package/.claude-plugin/plugin.json +3 -4
- package/.well-known/llms.txt +12 -33
- package/.well-known/mcp/server-card.json +8 -8
- package/README.md +30 -221
- package/adapters/chatgpt/openapi.yaml +1695 -0
- package/adapters/claude/.mcp.json +2 -2
- package/adapters/gemini/function-declarations.json +0 -1
- package/adapters/mcp/server-stdio.js +11 -263
- package/adapters/opencode/opencode.json +1 -1
- package/bench/thumbgate-bench.json +2 -2
- package/bin/cli.js +122 -1421
- package/bin/postinstall.js +27 -11
- package/config/gate-templates.json +0 -216
- package/config/gates/claim-verification.json +0 -12
- package/config/gates/default.json +2 -31
- package/config/github-about.json +2 -2
- package/config/mcp-allowlists.json +13 -23
- package/config/merge-quality-checks.json +1 -0
- package/config/model-candidates.json +6 -121
- package/openapi/openapi.yaml +0 -12
- package/package.json +99 -229
- package/public/blog.html +4 -4
- package/public/codex-plugin.html +20 -72
- package/public/compare.html +8 -31
- package/public/dashboard.html +166 -930
- package/public/federal.html +2 -2
- package/public/guide.html +9 -33
- package/public/index.html +116 -495
- package/public/learn.html +18 -191
- package/public/lessons.html +10 -168
- package/public/numbers.html +7 -7
- package/public/pro.html +11 -34
- package/scripts/agent-memory-lifecycle.js +0 -211
- package/scripts/agent-readiness.js +3 -167
- package/scripts/agent-reward-model.js +1 -53
- package/scripts/auto-promote-gates.js +10 -82
- package/scripts/auto-wire-hooks.js +0 -14
- package/scripts/billing.js +1 -93
- package/scripts/build-metadata.js +10 -50
- package/scripts/cli-feedback.js +6 -18
- package/scripts/cli-schema.js +0 -90
- package/scripts/cli-telemetry.js +1 -6
- package/scripts/commercial-offer.js +2 -82
- package/scripts/context-manager.js +6 -74
- package/scripts/dashboard.js +2 -68
- package/scripts/durability/step.js +3 -3
- package/scripts/export-databricks-bundle.js +1 -5
- package/scripts/export-dpo-pairs.js +2 -7
- package/scripts/feedback-loop.js +1 -145
- package/scripts/feedback-quality.js +0 -87
- package/scripts/filesystem-search.js +10 -35
- package/scripts/gate-stats.js +0 -89
- package/scripts/gates-engine.js +85 -1105
- package/scripts/gemini-embedding-policy.js +1 -2
- package/scripts/hook-runtime.js +14 -20
- package/scripts/hook-thumbgate-cache-updater.js +2 -18
- package/scripts/hybrid-feedback-context.js +7 -142
- package/scripts/lesson-inference.js +3 -8
- package/scripts/lesson-search.js +1 -17
- package/scripts/license.js +1 -0
- package/scripts/llm-client.js +9 -390
- package/scripts/local-model-profile.js +8 -15
- package/scripts/memory-scope-readiness.js +0 -159
- package/scripts/meta-agent-loop.js +0 -36
- package/scripts/operational-integrity.js +5 -39
- package/scripts/oss-pr-opportunity-scout.js +5 -35
- package/scripts/plausible-server-events.js +6 -9
- package/scripts/pro-local-dashboard.js +4 -4
- package/scripts/proxy-pointer-rag-guardrails.js +1 -42
- package/scripts/rate-limiter.js +13 -64
- package/scripts/secret-scanner.js +5 -44
- package/scripts/security-scanner.js +10 -260
- package/scripts/self-distill-agent.js +1 -3
- package/scripts/seo-gsd.js +2 -916
- package/scripts/statusline-cache-path.js +2 -17
- package/scripts/statusline-local-stats.js +1 -9
- package/scripts/statusline-meta.js +2 -28
- package/scripts/statusline.sh +4 -20
- package/scripts/telemetry-analytics.js +0 -357
- package/scripts/thompson-sampling.js +10 -31
- package/scripts/thumbgate-bench.js +1 -16
- package/scripts/thumbgate-search.js +19 -85
- package/scripts/tool-registry.js +1 -169
- package/scripts/vector-store.js +0 -45
- package/scripts/workflow-sentinel.js +53 -286
- package/scripts/workspace-evolver.js +2 -62
- package/src/api/server.js +290 -2578
- package/.claude/commands/dashboard.md +0 -15
- package/.claude/commands/thumbgate-blocked.md +0 -27
- package/.claude/commands/thumbgate-dashboard.md +0 -15
- package/.claude/commands/thumbgate-doctor.md +0 -30
- package/.claude/commands/thumbgate-guard.md +0 -36
- package/.claude/commands/thumbgate-protect.md +0 -30
- package/.claude/commands/thumbgate-rules.md +0 -30
- package/.well-known/agentic-verify.txt +0 -1
- package/adapters/gcp/dfcx-webhook-gate.js +0 -295
- package/adapters/letta/README.md +0 -41
- package/adapters/letta/thumbgate-letta-adapter.js +0 -133
- package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
- package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
- package/bench/observability-eval-suite.json +0 -26
- package/bin/dashboard-cli.js +0 -7
- package/commands/dashboard.md +0 -15
- package/commands/thumbgate-dashboard.md +0 -15
- package/config/builtin-lessons.json +0 -23
- package/config/gate-classifier-routing.json +0 -98
- package/config/post-deploy-marketing-pages.json +0 -61
- package/public/about.html +0 -162
- package/public/agent-manager.html +0 -179
- package/public/agents-cost-savings.html +0 -153
- package/public/ai-malpractice-prevention.html +0 -818
- package/public/assets/brand/github-social-preview.png +0 -0
- package/public/assets/brand/thumbgate-icon-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
- package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
- package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
- package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
- package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
- package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
- package/public/assets/brand/thumbgate-mark-team.svg +0 -26
- package/public/assets/brand/thumbgate-mark.svg +0 -21
- package/public/assets/brand/thumbgate-wordmark.svg +0 -20
- package/public/assets/claude-thumbgate-statusbar.svg +0 -8
- package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
- package/public/assets/legal-intake-control-flow.svg +0 -66
- package/public/brand/thumbgate-mark.svg +0 -19
- package/public/brand/thumbgate-og.svg +0 -16
- package/public/chatgpt-app.html +0 -330
- package/public/codex-enterprise.html +0 -123
- package/public/js/buyer-intent.js +0 -672
- package/public/pricing.html +0 -399
- package/scripts/action-receipts.js +0 -324
- package/scripts/activation-quickstart.js +0 -187
- package/scripts/agent-operations-planner.js +0 -621
- package/scripts/ai-component-inventory.js +0 -367
- package/scripts/async-eval-observability.js +0 -236
- package/scripts/audit.js +0 -65
- package/scripts/aws-blocks-guardrails.js +0 -228
- package/scripts/classifier-routing.js +0 -130
- package/scripts/dashboard-chat.js +0 -332
- package/scripts/feedback-aggregate.js +0 -281
- package/scripts/feedback-sanitizer.js +0 -105
- package/scripts/hob-pack.js +0 -591
- package/scripts/hook-stop-anti-claim.js +0 -301
- package/scripts/install-shim.js +0 -87
- package/scripts/mcp-oauth.js +0 -293
- package/scripts/noop-detect.js +0 -285
- package/scripts/omlx-smoke.js +0 -192
- package/scripts/parallel-workflow-orchestrator.js +0 -293
- package/scripts/plan-gate.js +0 -243
- package/scripts/plausible-domain-config.js +0 -99
- package/scripts/qa-scenario-planner.js +0 -136
- package/scripts/repeat-metric.js +0 -137
- package/scripts/secret-fixture-tokens.js +0 -61
- package/scripts/secret-redaction.js +0 -166
- package/scripts/self-harness-optimizer.js +0 -141
- package/scripts/self-healing-check.js +0 -193
- package/scripts/silent-failure-cluster.js +0 -531
- package/scripts/statusline-cache-read.js +0 -57
- package/scripts/sync-telemetry-from-prod.js +0 -374
- package/scripts/tool-contract-validator.js +0 -76
- package/scripts/trajectory-scorer.js +0 -63
- package/scripts/verify-marketing-pages-deployed.js +0 -195
- package/scripts/visitor-journey.js +0 -172
package/scripts/llm-client.js
CHANGED
|
@@ -4,94 +4,27 @@
|
|
|
4
4
|
const { runStep } = require('./durability/step');
|
|
5
5
|
|
|
6
6
|
const MODELS = {
|
|
7
|
-
FAST: '
|
|
8
|
-
SMART: '
|
|
9
|
-
ZAI_CODING: 'glm-5.2',
|
|
10
|
-
LEGACY_CLAUDE_FAST: 'claude-haiku-4-5-20251001',
|
|
11
|
-
LEGACY_CLAUDE_SMART: 'claude-sonnet-4-6',
|
|
7
|
+
FAST: 'claude-haiku-4-5-20251001',
|
|
8
|
+
SMART: 'claude-sonnet-4-6',
|
|
12
9
|
};
|
|
13
10
|
|
|
14
11
|
const DEFAULT_MODEL = MODELS.FAST;
|
|
15
12
|
const DEFAULT_MAX_TOKENS = 1024;
|
|
16
13
|
const DEFAULT_CACHE_TTL = '5m';
|
|
17
|
-
const DEFAULT_ZAI_BASE_URL = 'https://api.z.ai/api/coding/paas/v4';
|
|
18
|
-
const DEFAULT_ZAI_MODEL = MODELS.ZAI_CODING;
|
|
19
|
-
const DEFAULT_OMLX_BASE_URL = 'http://127.0.0.1:8000/v1';
|
|
20
|
-
const DEFAULT_OMLX_MODEL = 'qwen3-coder-next';
|
|
21
14
|
|
|
22
|
-
let
|
|
23
|
-
let _geminiClient = null;
|
|
15
|
+
let _client = null;
|
|
24
16
|
|
|
25
|
-
function
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function getZaiBaseUrl(env = process.env) {
|
|
30
|
-
return (env.ZAI_API_BASE_URL || env.THUMBGATE_ZAI_API_BASE_URL || DEFAULT_ZAI_BASE_URL).replace(/\/+$/, '');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function getZaiModel(env = process.env) {
|
|
34
|
-
return env.ZAI_API_MODEL || env.THUMBGATE_ZAI_MODEL || DEFAULT_ZAI_MODEL;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function getOmlxApiKey(env = process.env) {
|
|
38
|
-
return env.OMLX_API_KEY || env.THUMBGATE_OMLX_API_KEY || '';
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getOmlxBaseUrl(env = process.env) {
|
|
42
|
-
return (env.OMLX_BASE_URL || env.THUMBGATE_OMLX_BASE_URL || DEFAULT_OMLX_BASE_URL)
|
|
43
|
-
.replace(/\/chat\/completions\/?$/i, '')
|
|
44
|
-
.replace(/\/+$/, '');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getOmlxModel(env = process.env) {
|
|
48
|
-
return env.OMLX_MODEL || env.THUMBGATE_OMLX_MODEL || DEFAULT_OMLX_MODEL;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function hasZaiApiKey(env = process.env) {
|
|
52
|
-
return Boolean(getZaiApiKey(env));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function hasOmlxConfig(env = process.env) {
|
|
56
|
-
return env.THUMBGATE_LLM_PROVIDER === 'omlx'
|
|
57
|
-
|| env.LLM_PROVIDER === 'omlx'
|
|
58
|
-
|| env.THUMBGATE_OMLX_ENABLED === '1'
|
|
59
|
-
|| env.OMLX_ENABLED === '1'
|
|
60
|
-
|| Boolean(env.OMLX_BASE_URL || env.THUMBGATE_OMLX_BASE_URL);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function isZaiProvider(provider = '') {
|
|
64
|
-
return /^(zai|z\.ai|glm|glm-coding)$/i.test(String(provider || '').trim());
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function isOmlxProvider(provider = '') {
|
|
68
|
-
return /^(omlx|local-omlx|mlx-local)$/i.test(String(provider || '').trim());
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function isZaiModel(model = '') {
|
|
72
|
-
return /^(glm|zai|z\.ai)/i.test(String(model || '').trim());
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function isAvailable(provider = '') {
|
|
76
|
-
const normalizedProvider = String(provider || '').trim().toLowerCase();
|
|
77
|
-
if (isZaiProvider(normalizedProvider)) return hasZaiApiKey();
|
|
78
|
-
if (isOmlxProvider(normalizedProvider)) return hasOmlxConfig();
|
|
79
|
-
if (normalizedProvider === 'anthropic' || normalizedProvider === 'claude') {
|
|
80
|
-
return Boolean(process.env.ANTHROPIC_API_KEY);
|
|
81
|
-
}
|
|
82
|
-
if (normalizedProvider === 'gemini' || normalizedProvider === 'vertex') {
|
|
83
|
-
return Boolean(process.env.GEMINI_API_KEY || process.env.VERTEX_PROJECT_ID);
|
|
84
|
-
}
|
|
85
|
-
return Boolean(hasOmlxConfig() || hasZaiApiKey() || process.env.ANTHROPIC_API_KEY || process.env.GEMINI_API_KEY || process.env.VERTEX_PROJECT_ID);
|
|
17
|
+
function isAvailable() {
|
|
18
|
+
return Boolean(process.env.ANTHROPIC_API_KEY);
|
|
86
19
|
}
|
|
87
20
|
|
|
88
21
|
function getClient() {
|
|
89
|
-
if (
|
|
90
|
-
if (!isAvailable(
|
|
22
|
+
if (_client) return _client;
|
|
23
|
+
if (!isAvailable()) return null;
|
|
91
24
|
try {
|
|
92
25
|
const Anthropic = require('@anthropic-ai/sdk');
|
|
93
|
-
|
|
94
|
-
return
|
|
26
|
+
_client = new Anthropic();
|
|
27
|
+
return _client;
|
|
95
28
|
} catch {
|
|
96
29
|
return null;
|
|
97
30
|
}
|
|
@@ -189,63 +122,6 @@ function buildClaudeRequest({
|
|
|
189
122
|
return request;
|
|
190
123
|
}
|
|
191
124
|
|
|
192
|
-
function normalizeMessageText(content) {
|
|
193
|
-
if (typeof content === 'string') return content;
|
|
194
|
-
if (Array.isArray(content)) {
|
|
195
|
-
return content.map((part) => {
|
|
196
|
-
if (typeof part === 'string') return part;
|
|
197
|
-
if (part && typeof part === 'object') return part.text || JSON.stringify(part);
|
|
198
|
-
return '';
|
|
199
|
-
}).join('');
|
|
200
|
-
}
|
|
201
|
-
if (content && typeof content === 'object') {
|
|
202
|
-
return content.text || JSON.stringify(content);
|
|
203
|
-
}
|
|
204
|
-
return '';
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
function convertMessagesToOpenAi(messages, userPrompt, systemPrompt) {
|
|
208
|
-
const normalized = [];
|
|
209
|
-
if (systemPrompt) normalized.push({ role: 'system', content: systemPrompt });
|
|
210
|
-
|
|
211
|
-
const list = Array.isArray(messages) && messages.length > 0
|
|
212
|
-
? messages
|
|
213
|
-
: [{ role: 'user', content: userPrompt }];
|
|
214
|
-
|
|
215
|
-
for (const message of list) {
|
|
216
|
-
if (!message || typeof message !== 'object') continue;
|
|
217
|
-
const role = message.role === 'assistant' || message.role === 'system' ? message.role : 'user';
|
|
218
|
-
const content = normalizeMessageText(message.content);
|
|
219
|
-
if (content) normalized.push({ role, content });
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
if (normalized.length === 0) {
|
|
223
|
-
normalized.push({ role: 'user', content: String(userPrompt || '') });
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return normalized;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function buildOpenAiCompatibleRequest({
|
|
230
|
-
systemPrompt,
|
|
231
|
-
userPrompt,
|
|
232
|
-
messages,
|
|
233
|
-
model,
|
|
234
|
-
maxTokens,
|
|
235
|
-
temperature,
|
|
236
|
-
metadata,
|
|
237
|
-
} = {}, env = process.env) {
|
|
238
|
-
const request = {
|
|
239
|
-
model: model || getZaiModel(env),
|
|
240
|
-
messages: convertMessagesToOpenAi(messages, userPrompt, systemPrompt),
|
|
241
|
-
max_tokens: maxTokens || DEFAULT_MAX_TOKENS,
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
if (Number.isFinite(temperature)) request.temperature = temperature;
|
|
245
|
-
if (metadata && typeof metadata === 'object') request.metadata = metadata;
|
|
246
|
-
return request;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
125
|
function extractTextContent(response) {
|
|
250
126
|
return (response?.content || [])
|
|
251
127
|
.filter((block) => block.type === 'text')
|
|
@@ -262,192 +138,7 @@ function parseClaudeJson(text) {
|
|
|
262
138
|
}
|
|
263
139
|
}
|
|
264
140
|
|
|
265
|
-
async function callZaiInternal(options = {}) {
|
|
266
|
-
const apiKey = getZaiApiKey();
|
|
267
|
-
if (!apiKey) return null;
|
|
268
|
-
if (typeof fetch !== 'function') return null;
|
|
269
|
-
|
|
270
|
-
const baseUrl = getZaiBaseUrl();
|
|
271
|
-
const requestedModel = options.model && isZaiModel(options.model) ? options.model : undefined;
|
|
272
|
-
const request = buildOpenAiCompatibleRequest({
|
|
273
|
-
...options,
|
|
274
|
-
model: requestedModel || getZaiModel(),
|
|
275
|
-
});
|
|
276
|
-
|
|
277
|
-
try {
|
|
278
|
-
const response = await runStep('llm.callZai', {
|
|
279
|
-
retries: 2,
|
|
280
|
-
logger: (msg) => console.warn(msg),
|
|
281
|
-
}, async () => fetch(`${baseUrl}/chat/completions`, {
|
|
282
|
-
method: 'POST',
|
|
283
|
-
headers: {
|
|
284
|
-
Authorization: `Bearer ${apiKey}`,
|
|
285
|
-
'Content-Type': 'application/json',
|
|
286
|
-
},
|
|
287
|
-
body: JSON.stringify(request),
|
|
288
|
-
}));
|
|
289
|
-
|
|
290
|
-
if (!response || !response.ok) {
|
|
291
|
-
throw new Error(`Z.ai request failed with status ${response?.status || 'unknown'}`);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
const json = await response.json();
|
|
295
|
-
const text = stripCodeFences(json?.choices?.[0]?.message?.content || '');
|
|
296
|
-
return {
|
|
297
|
-
text,
|
|
298
|
-
usage: json?.usage || null,
|
|
299
|
-
stopReason: json?.choices?.[0]?.finish_reason || null,
|
|
300
|
-
id: json?.id || null,
|
|
301
|
-
model: json?.model || request.model,
|
|
302
|
-
provider: 'zai',
|
|
303
|
-
};
|
|
304
|
-
} catch (err) {
|
|
305
|
-
console.error('Z.ai execution error:', err?.message || err);
|
|
306
|
-
return null;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
async function callOmlxInternal(options = {}) {
|
|
311
|
-
if (!hasOmlxConfig()) return null;
|
|
312
|
-
if (typeof fetch !== 'function') return null;
|
|
313
|
-
|
|
314
|
-
const baseUrl = getOmlxBaseUrl();
|
|
315
|
-
const request = buildOpenAiCompatibleRequest({
|
|
316
|
-
...options,
|
|
317
|
-
model: options.model || getOmlxModel(),
|
|
318
|
-
});
|
|
319
|
-
const apiKey = getOmlxApiKey();
|
|
320
|
-
const headers = {
|
|
321
|
-
'Content-Type': 'application/json',
|
|
322
|
-
};
|
|
323
|
-
if (apiKey) headers.Authorization = `Bearer ${apiKey}`;
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
const response = await runStep('llm.callOmlx', {
|
|
327
|
-
retries: 1,
|
|
328
|
-
logger: (msg) => console.warn(msg),
|
|
329
|
-
}, async () => fetch(`${baseUrl}/chat/completions`, {
|
|
330
|
-
method: 'POST',
|
|
331
|
-
headers,
|
|
332
|
-
body: JSON.stringify(request),
|
|
333
|
-
}));
|
|
334
|
-
|
|
335
|
-
if (!response || !response.ok) {
|
|
336
|
-
throw new Error(`oMLX request failed with status ${response?.status || 'unknown'}`);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
const json = await response.json();
|
|
340
|
-
const text = stripCodeFences(json?.choices?.[0]?.message?.content || '');
|
|
341
|
-
return {
|
|
342
|
-
text,
|
|
343
|
-
usage: json?.usage || null,
|
|
344
|
-
stopReason: json?.choices?.[0]?.finish_reason || null,
|
|
345
|
-
id: json?.id || null,
|
|
346
|
-
model: json?.model || request.model,
|
|
347
|
-
provider: 'omlx',
|
|
348
|
-
};
|
|
349
|
-
} catch (err) {
|
|
350
|
-
console.error('oMLX execution error:', err?.message || err);
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async function callGeminiInternal(options = {}) {
|
|
356
|
-
const env = process.env;
|
|
357
|
-
const { detectInferenceBackend } = require('./local-model-profile');
|
|
358
|
-
const providerMode = detectInferenceBackend(env).providerMode;
|
|
359
|
-
|
|
360
|
-
try {
|
|
361
|
-
const { GoogleGenAI } = require('@google/genai');
|
|
362
|
-
if (!_geminiClient) {
|
|
363
|
-
if (providerMode === 'vertex') {
|
|
364
|
-
_geminiClient = new GoogleGenAI({
|
|
365
|
-
enterprise: true,
|
|
366
|
-
project: env.VERTEX_PROJECT_ID || 'ai-revenue28-webhook',
|
|
367
|
-
location: env.VERTEX_LOCATION || 'us-central1',
|
|
368
|
-
});
|
|
369
|
-
} else {
|
|
370
|
-
_geminiClient = new GoogleGenAI({
|
|
371
|
-
apiKey: env.GEMINI_API_KEY,
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const contents = convertMessagesToGemini(options.messages, options.userPrompt);
|
|
377
|
-
const config = {};
|
|
378
|
-
if (options.systemPrompt) {
|
|
379
|
-
config.systemInstruction = options.systemPrompt;
|
|
380
|
-
}
|
|
381
|
-
if (Number.isFinite(options.temperature)) {
|
|
382
|
-
config.temperature = options.temperature;
|
|
383
|
-
}
|
|
384
|
-
if (options.maxTokens) {
|
|
385
|
-
config.maxOutputTokens = options.maxTokens;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
const response = await runStep('llm.callGemini', {
|
|
389
|
-
retries: 2,
|
|
390
|
-
logger: (msg) => console.warn(msg),
|
|
391
|
-
}, async () => _geminiClient.models.generateContent({
|
|
392
|
-
model: options.model,
|
|
393
|
-
contents,
|
|
394
|
-
config,
|
|
395
|
-
}));
|
|
396
|
-
|
|
397
|
-
return {
|
|
398
|
-
text: response.text || '',
|
|
399
|
-
usage: response.usageMetadata ? {
|
|
400
|
-
input_tokens: response.usageMetadata.promptTokenCount,
|
|
401
|
-
output_tokens: response.usageMetadata.candidatesTokenCount,
|
|
402
|
-
} : null,
|
|
403
|
-
stopReason: response.candidates?.[0]?.finishReason || null,
|
|
404
|
-
id: null,
|
|
405
|
-
model: options.model,
|
|
406
|
-
};
|
|
407
|
-
} catch (err) {
|
|
408
|
-
console.error('Gemini/Vertex AI execution error:', err);
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function convertMessagesToGemini(messages, userPrompt) {
|
|
414
|
-
const list = Array.isArray(messages) && messages.length > 0
|
|
415
|
-
? messages
|
|
416
|
-
: [{ role: 'user', content: userPrompt }];
|
|
417
|
-
|
|
418
|
-
return list.map((msg) => {
|
|
419
|
-
const role = msg.role === 'assistant' ? 'model' : 'user';
|
|
420
|
-
let text = '';
|
|
421
|
-
if (typeof msg.content === 'string') {
|
|
422
|
-
text = msg.content;
|
|
423
|
-
} else if (Array.isArray(msg.content)) {
|
|
424
|
-
text = msg.content.map((c) => c.text || '').join('');
|
|
425
|
-
} else if (msg.content && typeof msg.content === 'object') {
|
|
426
|
-
text = msg.content.text || JSON.stringify(msg.content);
|
|
427
|
-
}
|
|
428
|
-
return {
|
|
429
|
-
role,
|
|
430
|
-
parts: [{ text }],
|
|
431
|
-
};
|
|
432
|
-
});
|
|
433
|
-
}
|
|
434
|
-
|
|
435
141
|
async function callClaudeInternal(options = {}) {
|
|
436
|
-
const modelName = options.model || '';
|
|
437
|
-
const provider = options.provider || process.env.THUMBGATE_LLM_PROVIDER || process.env.LLM_PROVIDER || '';
|
|
438
|
-
const requestedZai = isZaiProvider(provider) || isZaiModel(modelName);
|
|
439
|
-
if (isOmlxProvider(provider)) {
|
|
440
|
-
return callOmlxInternal(options);
|
|
441
|
-
}
|
|
442
|
-
if (requestedZai || (!process.env.ANTHROPIC_API_KEY && hasZaiApiKey() && !modelName.startsWith('gemini') && !modelName.startsWith('vertex'))) {
|
|
443
|
-
const zaiOptions = requestedZai ? options : { ...options, model: undefined };
|
|
444
|
-
return callZaiInternal(zaiOptions);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
if (modelName.startsWith('gemini') || modelName.startsWith('vertex')) {
|
|
448
|
-
return callGeminiInternal(options);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
142
|
const client = getClient();
|
|
452
143
|
if (!client) return null;
|
|
453
144
|
|
|
@@ -496,63 +187,6 @@ async function callClaudeJson(options = {}) {
|
|
|
496
187
|
stopReason: result.stopReason,
|
|
497
188
|
id: result.id,
|
|
498
189
|
model: result.model,
|
|
499
|
-
provider: result.provider || 'anthropic',
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
return parsed;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
async function callZai(options = {}) {
|
|
507
|
-
const result = await callZaiInternal(options);
|
|
508
|
-
if (!result) return null;
|
|
509
|
-
return options.returnMetadata ? result : result.text;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
async function callZaiJson(options = {}) {
|
|
513
|
-
const result = await callZaiInternal(options);
|
|
514
|
-
if (!result) return null;
|
|
515
|
-
|
|
516
|
-
const parsed = parseClaudeJson(result.text);
|
|
517
|
-
if (parsed === null) return null;
|
|
518
|
-
|
|
519
|
-
if (options.returnMetadata) {
|
|
520
|
-
return {
|
|
521
|
-
parsed,
|
|
522
|
-
text: result.text,
|
|
523
|
-
usage: result.usage,
|
|
524
|
-
stopReason: result.stopReason,
|
|
525
|
-
id: result.id,
|
|
526
|
-
model: result.model,
|
|
527
|
-
provider: result.provider,
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return parsed;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
async function callOmlx(options = {}) {
|
|
535
|
-
const result = await callOmlxInternal(options);
|
|
536
|
-
if (!result) return null;
|
|
537
|
-
return options.returnMetadata ? result : result.text;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
async function callOmlxJson(options = {}) {
|
|
541
|
-
const result = await callOmlxInternal(options);
|
|
542
|
-
if (!result) return null;
|
|
543
|
-
|
|
544
|
-
const parsed = parseClaudeJson(result.text);
|
|
545
|
-
if (parsed === null) return null;
|
|
546
|
-
|
|
547
|
-
if (options.returnMetadata) {
|
|
548
|
-
return {
|
|
549
|
-
parsed,
|
|
550
|
-
text: result.text,
|
|
551
|
-
usage: result.usage,
|
|
552
|
-
stopReason: result.stopReason,
|
|
553
|
-
id: result.id,
|
|
554
|
-
model: result.model,
|
|
555
|
-
provider: result.provider,
|
|
556
190
|
};
|
|
557
191
|
}
|
|
558
192
|
|
|
@@ -563,24 +197,9 @@ module.exports = {
|
|
|
563
197
|
isAvailable,
|
|
564
198
|
callClaude,
|
|
565
199
|
callClaudeJson,
|
|
566
|
-
callZai,
|
|
567
|
-
callZaiJson,
|
|
568
|
-
callOmlx,
|
|
569
|
-
callOmlxJson,
|
|
570
200
|
stripCodeFences,
|
|
571
201
|
parseClaudeJson,
|
|
572
202
|
normalizeCacheOptions,
|
|
573
203
|
buildClaudeRequest,
|
|
574
|
-
buildOpenAiCompatibleRequest,
|
|
575
|
-
convertMessagesToOpenAi,
|
|
576
|
-
getZaiApiKey,
|
|
577
|
-
getZaiBaseUrl,
|
|
578
|
-
getZaiModel,
|
|
579
|
-
getOmlxApiKey,
|
|
580
|
-
getOmlxBaseUrl,
|
|
581
|
-
getOmlxModel,
|
|
582
204
|
MODELS,
|
|
583
|
-
DEFAULT_ZAI_BASE_URL,
|
|
584
|
-
DEFAULT_OMLX_BASE_URL,
|
|
585
|
-
DEFAULT_OMLX_MODEL,
|
|
586
205
|
};
|
|
@@ -111,8 +111,7 @@ function isSparseAttentionFamily(modelFamily) {
|
|
|
111
111
|
|
|
112
112
|
function resolveProviderMode(env = process.env) {
|
|
113
113
|
const explicit = normalizeSlug(env.THUMBGATE_PROVIDER_MODE || env.THUMBGATE_MODEL_PROVIDER_MODE);
|
|
114
|
-
if (explicit === 'local' || explicit === 'managed'
|
|
115
|
-
if (env.VERTEX_PROJECT_ID || env.VERTEX_API_ENDPOINT) return 'vertex';
|
|
114
|
+
if (explicit === 'local' || explicit === 'managed') return explicit;
|
|
116
115
|
if (env.THUMBGATE_LOCAL_MODEL_FAMILY || env.THUMBGATE_LOCAL_MODEL_SERVER) return 'local';
|
|
117
116
|
return 'managed';
|
|
118
117
|
}
|
|
@@ -134,7 +133,6 @@ function resolveModelFamily(env = process.env) {
|
|
|
134
133
|
}
|
|
135
134
|
|
|
136
135
|
function buildBackendLabel(providerMode, modelFamily) {
|
|
137
|
-
if (providerMode === 'vertex') return 'Vertex AI secure cloud backend';
|
|
138
136
|
if (providerMode === 'managed') return 'Managed API backend';
|
|
139
137
|
if (modelFamily.startsWith('deepseek')) return 'Local DeepSeek sparse backend';
|
|
140
138
|
if (modelFamily.startsWith('glm')) return 'Local GLM sparse backend';
|
|
@@ -150,18 +148,14 @@ function detectInferenceBackend(env = process.env) {
|
|
|
150
148
|
&& supportsSparseAttention
|
|
151
149
|
&& INDEXCACHE_SERVER_ENGINES.has(serverEngine);
|
|
152
150
|
const indexCacheEnabled = indexCacheEligible && parseBoolean(env.THUMBGATE_INDEXCACHE_ENABLED, false);
|
|
153
|
-
const id = providerMode === '
|
|
154
|
-
? '
|
|
155
|
-
:
|
|
156
|
-
?
|
|
157
|
-
:
|
|
158
|
-
? `local-${modelFamily}-sparse`
|
|
159
|
-
: 'local-dense';
|
|
151
|
+
const id = providerMode === 'managed'
|
|
152
|
+
? 'managed-api'
|
|
153
|
+
: supportsSparseAttention
|
|
154
|
+
? `local-${modelFamily}-sparse`
|
|
155
|
+
: 'local-dense';
|
|
160
156
|
|
|
161
157
|
let rationale = 'Baseline backend with no sparse-attention acceleration.';
|
|
162
|
-
if (providerMode === '
|
|
163
|
-
rationale = 'Vertex AI secure cloud backend providing compliant enterprise Gemini models inside VPC boundary.';
|
|
164
|
-
} else if (providerMode === 'managed') {
|
|
158
|
+
if (providerMode === 'managed') {
|
|
165
159
|
rationale = 'Managed API path does not expose sparse-attention kernel controls like IndexCache.';
|
|
166
160
|
} else if (indexCacheEnabled) {
|
|
167
161
|
rationale = `Local ${modelFamily} backend is sparse-attention capable and IndexCache-ready on ${serverEngine}.`;
|
|
@@ -342,8 +336,7 @@ function resolveModelRole(role, env) {
|
|
|
342
336
|
const envKey = `THUMBGATE_MODEL_ROLE_${normalized.toUpperCase()}`;
|
|
343
337
|
const modelFamily = resolveModelFamily(e);
|
|
344
338
|
const isLocalGlm = modelFamily.startsWith('glm');
|
|
345
|
-
const
|
|
346
|
-
const provider = isLocalGlm ? 'local' : (providerMode === 'vertex' ? 'vertex' : 'gemini');
|
|
339
|
+
const provider = isLocalGlm ? 'local' : 'gemini';
|
|
347
340
|
const defaultModel = isLocalGlm ? (GLM_MODEL_ROLES[normalized] || MODEL_ROLES[normalized]) : MODEL_ROLES[normalized];
|
|
348
341
|
const model = (e[envKey] && String(e[envKey]).trim()) || defaultModel;
|
|
349
342
|
return { role: normalized, model, provider, envKey };
|
|
@@ -2,38 +2,6 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const REQUIRED_SCOPE_FIELDS = ['entityId', 'projectId', 'processId', 'sessionId'];
|
|
5
|
-
const MEMORY_OS_LAYERS = Object.freeze([
|
|
6
|
-
{
|
|
7
|
-
id: 'file_layer',
|
|
8
|
-
name: 'File Layer',
|
|
9
|
-
purpose: 'Raw feedback, tool receipts, sessions, and memory rows are durably stored before interpretation.',
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
id: 'vector_db_layer',
|
|
13
|
-
name: 'Vector DB Layer',
|
|
14
|
-
purpose: 'Semantic retrieval can find related lessons without stuffing every raw memory into context.',
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
id: 'structured_facts_layer',
|
|
18
|
-
name: 'Structured Facts Layer',
|
|
19
|
-
purpose: 'Confirmed account, project, policy, and budget facts are typed separately from fuzzy memories.',
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: 'auto_curation_layer',
|
|
23
|
-
name: 'Auto Curation Layer',
|
|
24
|
-
purpose: 'Duplicate, stale, contradictory, and unscoped memories are consolidated before retrieval quality decays.',
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: 'context_layer',
|
|
28
|
-
name: 'Context Layer',
|
|
29
|
-
purpose: 'Only relevant scoped memories enter a given tool call, PR, deployment, or support session.',
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
id: 'interface_layer',
|
|
33
|
-
name: 'Interface Layer',
|
|
34
|
-
purpose: 'The memory contract is exposed through CLI, MCP, hooks, dashboards, and agent adapters without model lock-in.',
|
|
35
|
-
},
|
|
36
|
-
]);
|
|
37
5
|
|
|
38
6
|
const FIELD_ALIASES = {
|
|
39
7
|
entityId: [
|
|
@@ -260,128 +228,6 @@ function buildRecommendations({ unscopedRecords, crossScopeDuplicates }) {
|
|
|
260
228
|
return recommendations;
|
|
261
229
|
}
|
|
262
230
|
|
|
263
|
-
function hasEmbeddingEvidence(record = {}) {
|
|
264
|
-
return Boolean(
|
|
265
|
-
record.embedding
|
|
266
|
-
|| record.vector
|
|
267
|
-
|| record.embeddingId
|
|
268
|
-
|| record.metadata?.embedding
|
|
269
|
-
|| record.metadata?.embeddingId
|
|
270
|
-
|| record.metadata?.vectorId
|
|
271
|
-
|| record.semanticKey
|
|
272
|
-
|| record.metadata?.semanticKey
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function hasStructuredFactEvidence(record = {}) {
|
|
277
|
-
const type = String(record.type || record.kind || record.memoryType || record.metadata?.type || '').toLowerCase();
|
|
278
|
-
return type === 'fact'
|
|
279
|
-
|| type === 'structured_fact'
|
|
280
|
-
|| Boolean(record.factKey || record.fact || record.metadata?.factKey || record.metadata?.fact);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
function hasContextEvidence(record = {}) {
|
|
284
|
-
return Boolean(
|
|
285
|
-
record.contextPackId
|
|
286
|
-
|| record.contextPack
|
|
287
|
-
|| record.metadata?.contextPackId
|
|
288
|
-
|| record.metadata?.contextPack
|
|
289
|
-
|| record.retrievalQuery
|
|
290
|
-
|| record.metadata?.retrievalQuery
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
function boolCapability(capabilities = {}, ...keys) {
|
|
295
|
-
return keys.some((key) => capabilities[key] === true);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
function buildMemoryOsLayerReport(records = [], capabilities = {}) {
|
|
299
|
-
const scopeReport = buildMemoryScopeReadinessReport(records);
|
|
300
|
-
const semanticRecords = records.filter(hasEmbeddingEvidence);
|
|
301
|
-
const structuredFactRecords = records.filter(hasStructuredFactEvidence);
|
|
302
|
-
const contextRecords = records.filter(hasContextEvidence);
|
|
303
|
-
const curationReady = scopeReport.unscopedRecords === 0 && scopeReport.crossScopeDuplicates.length === 0;
|
|
304
|
-
|
|
305
|
-
const checks = [
|
|
306
|
-
{
|
|
307
|
-
id: 'file_layer',
|
|
308
|
-
ok: records.length > 0 || boolCapability(capabilities, 'rawStorage', 'fileLayer'),
|
|
309
|
-
evidence: {
|
|
310
|
-
records: records.length,
|
|
311
|
-
durableStore: Boolean(records.length > 0 || capabilities.rawStorage || capabilities.fileLayer),
|
|
312
|
-
},
|
|
313
|
-
recommendation: 'Capture raw feedback, action receipts, and tool outcomes before promoting memories.',
|
|
314
|
-
},
|
|
315
|
-
{
|
|
316
|
-
id: 'vector_db_layer',
|
|
317
|
-
ok: semanticRecords.length > 0 || boolCapability(capabilities, 'semanticSearch', 'vectorDbLayer'),
|
|
318
|
-
evidence: {
|
|
319
|
-
semanticRecords: semanticRecords.length,
|
|
320
|
-
semanticSearch: Boolean(capabilities.semanticSearch || capabilities.vectorDbLayer),
|
|
321
|
-
},
|
|
322
|
-
recommendation: 'Index lessons with semantic keys or embeddings so related failures are retrieved before action.',
|
|
323
|
-
},
|
|
324
|
-
{
|
|
325
|
-
id: 'structured_facts_layer',
|
|
326
|
-
ok: structuredFactRecords.length > 0 || boolCapability(capabilities, 'structuredFacts', 'structuredFactsLayer'),
|
|
327
|
-
evidence: {
|
|
328
|
-
structuredFactRecords: structuredFactRecords.length,
|
|
329
|
-
structuredFacts: Boolean(capabilities.structuredFacts || capabilities.structuredFactsLayer),
|
|
330
|
-
},
|
|
331
|
-
recommendation: 'Store confirmed customer, project, policy, and budget facts as typed records, not just prose.',
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
id: 'auto_curation_layer',
|
|
335
|
-
ok: curationReady && boolCapability(capabilities, 'autoCuration', 'dedupe', 'autoCurationLayer'),
|
|
336
|
-
evidence: {
|
|
337
|
-
unscopedRecords: scopeReport.unscopedRecords,
|
|
338
|
-
crossScopeDuplicates: scopeReport.crossScopeDuplicates.length,
|
|
339
|
-
autoCuration: Boolean(capabilities.autoCuration || capabilities.dedupe || capabilities.autoCurationLayer),
|
|
340
|
-
},
|
|
341
|
-
recommendation: 'Run dedupe, contradiction, stale-memory, and scope-isolation checks before memories can become gates.',
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
id: 'context_layer',
|
|
345
|
-
ok: contextRecords.length > 0 || boolCapability(capabilities, 'contextPacks', 'contextLayer', 'scopedRetrieval'),
|
|
346
|
-
evidence: {
|
|
347
|
-
contextRecords: contextRecords.length,
|
|
348
|
-
scopedRetrieval: Boolean(capabilities.contextPacks || capabilities.contextLayer || capabilities.scopedRetrieval),
|
|
349
|
-
},
|
|
350
|
-
recommendation: 'Inject scoped context packs per task instead of loading every memory into the model window.',
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
id: 'interface_layer',
|
|
354
|
-
ok: boolCapability(capabilities, 'mcp', 'cli', 'hooks', 'dashboard', 'interfaceLayer'),
|
|
355
|
-
evidence: {
|
|
356
|
-
cli: Boolean(capabilities.cli),
|
|
357
|
-
mcp: Boolean(capabilities.mcp),
|
|
358
|
-
hooks: Boolean(capabilities.hooks),
|
|
359
|
-
dashboard: Boolean(capabilities.dashboard),
|
|
360
|
-
},
|
|
361
|
-
recommendation: 'Expose the same memory contract through CLI, MCP, hooks, dashboard, and agent adapters.',
|
|
362
|
-
},
|
|
363
|
-
].map((check) => {
|
|
364
|
-
const layer = MEMORY_OS_LAYERS.find((candidate) => candidate.id === check.id);
|
|
365
|
-
return {
|
|
366
|
-
...layer,
|
|
367
|
-
...check,
|
|
368
|
-
};
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
const missingLayers = checks.filter((check) => !check.ok).map((check) => check.id);
|
|
372
|
-
|
|
373
|
-
return {
|
|
374
|
-
ready: missingLayers.length === 0,
|
|
375
|
-
riskLevel: missingLayers.length === 0 ? 'low' : missingLayers.length <= 2 ? 'medium' : 'high',
|
|
376
|
-
layers: checks,
|
|
377
|
-
missingLayers,
|
|
378
|
-
scopeReport,
|
|
379
|
-
recommendations: checks
|
|
380
|
-
.filter((check) => !check.ok)
|
|
381
|
-
.map((check) => check.recommendation),
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
|
|
385
231
|
function selectRecordsForScope(records = [], requestedScope = {}, options = {}) {
|
|
386
232
|
const requested = normalizeScope(requestedScope);
|
|
387
233
|
const requestedKey = memoryScopeKey(requested);
|
|
@@ -419,7 +265,6 @@ function buildMemoriStyleBenchmarkRecords() {
|
|
|
419
265
|
projectId: 'thumbgate',
|
|
420
266
|
processId: 'agent-a',
|
|
421
267
|
sessionId: 'session-1',
|
|
422
|
-
metadata: { semanticKey: 'checkout-readiness', contextPackId: 'checkout-pro' },
|
|
423
268
|
content: 'Use the paid sprint checklist before changing checkout code.',
|
|
424
269
|
},
|
|
425
270
|
{
|
|
@@ -453,18 +298,14 @@ function buildMemoriStyleBenchmarkRecords() {
|
|
|
453
298
|
processId: 'agent-a',
|
|
454
299
|
sessionId: 'session-1',
|
|
455
300
|
visibility: 'shared',
|
|
456
|
-
type: 'fact',
|
|
457
|
-
factKey: 'checkout.mutation_policy',
|
|
458
301
|
content: 'Shared rule: checkout mutations require audit evidence.',
|
|
459
302
|
},
|
|
460
303
|
];
|
|
461
304
|
}
|
|
462
305
|
|
|
463
306
|
module.exports = {
|
|
464
|
-
MEMORY_OS_LAYERS,
|
|
465
307
|
REQUIRED_SCOPE_FIELDS,
|
|
466
308
|
buildMemoriStyleBenchmarkRecords,
|
|
467
|
-
buildMemoryOsLayerReport,
|
|
468
309
|
buildMemoryScopeReadinessReport,
|
|
469
310
|
isSharedMemory,
|
|
470
311
|
memoryScopeKey,
|