thumbgate 1.27.16 → 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.
Files changed (165) hide show
  1. package/.claude-plugin/marketplace.json +85 -0
  2. package/.claude-plugin/plugin.json +3 -4
  3. package/.well-known/llms.txt +12 -33
  4. package/.well-known/mcp/server-card.json +8 -8
  5. package/README.md +30 -215
  6. package/adapters/chatgpt/openapi.yaml +1695 -0
  7. package/adapters/claude/.mcp.json +2 -2
  8. package/adapters/gemini/function-declarations.json +0 -1
  9. package/adapters/mcp/server-stdio.js +11 -263
  10. package/adapters/opencode/opencode.json +1 -1
  11. package/bench/thumbgate-bench.json +2 -2
  12. package/bin/cli.js +122 -1421
  13. package/bin/postinstall.js +27 -11
  14. package/config/gate-templates.json +0 -216
  15. package/config/gates/claim-verification.json +0 -12
  16. package/config/gates/default.json +2 -31
  17. package/config/github-about.json +2 -2
  18. package/config/mcp-allowlists.json +13 -23
  19. package/config/merge-quality-checks.json +1 -0
  20. package/config/model-candidates.json +6 -121
  21. package/openapi/openapi.yaml +0 -12
  22. package/package.json +98 -228
  23. package/public/blog.html +4 -4
  24. package/public/codex-plugin.html +20 -72
  25. package/public/compare.html +8 -31
  26. package/public/dashboard.html +166 -930
  27. package/public/federal.html +2 -2
  28. package/public/guide.html +9 -33
  29. package/public/index.html +116 -495
  30. package/public/learn.html +18 -191
  31. package/public/lessons.html +10 -168
  32. package/public/numbers.html +7 -7
  33. package/public/pro.html +11 -34
  34. package/scripts/agent-memory-lifecycle.js +0 -211
  35. package/scripts/agent-readiness.js +3 -167
  36. package/scripts/agent-reward-model.js +1 -53
  37. package/scripts/auto-promote-gates.js +10 -82
  38. package/scripts/auto-wire-hooks.js +0 -14
  39. package/scripts/billing.js +1 -93
  40. package/scripts/build-metadata.js +10 -50
  41. package/scripts/cli-feedback.js +6 -18
  42. package/scripts/cli-schema.js +0 -90
  43. package/scripts/cli-telemetry.js +1 -6
  44. package/scripts/commercial-offer.js +2 -82
  45. package/scripts/context-manager.js +6 -74
  46. package/scripts/dashboard.js +2 -68
  47. package/scripts/durability/step.js +3 -3
  48. package/scripts/export-databricks-bundle.js +1 -5
  49. package/scripts/export-dpo-pairs.js +2 -7
  50. package/scripts/feedback-loop.js +1 -123
  51. package/scripts/feedback-quality.js +0 -87
  52. package/scripts/filesystem-search.js +10 -35
  53. package/scripts/gate-stats.js +0 -89
  54. package/scripts/gates-engine.js +85 -1105
  55. package/scripts/gemini-embedding-policy.js +1 -2
  56. package/scripts/hook-runtime.js +14 -20
  57. package/scripts/hook-thumbgate-cache-updater.js +2 -18
  58. package/scripts/hybrid-feedback-context.js +7 -142
  59. package/scripts/lesson-inference.js +3 -8
  60. package/scripts/lesson-search.js +1 -17
  61. package/scripts/license.js +1 -0
  62. package/scripts/llm-client.js +9 -390
  63. package/scripts/local-model-profile.js +8 -15
  64. package/scripts/memory-scope-readiness.js +0 -159
  65. package/scripts/meta-agent-loop.js +0 -36
  66. package/scripts/operational-integrity.js +5 -39
  67. package/scripts/oss-pr-opportunity-scout.js +5 -35
  68. package/scripts/plausible-server-events.js +6 -9
  69. package/scripts/pro-local-dashboard.js +4 -4
  70. package/scripts/proxy-pointer-rag-guardrails.js +1 -42
  71. package/scripts/rate-limiter.js +13 -64
  72. package/scripts/secret-scanner.js +5 -44
  73. package/scripts/security-scanner.js +10 -260
  74. package/scripts/self-distill-agent.js +1 -3
  75. package/scripts/seo-gsd.js +2 -916
  76. package/scripts/statusline-cache-path.js +2 -17
  77. package/scripts/statusline-local-stats.js +1 -9
  78. package/scripts/statusline-meta.js +2 -28
  79. package/scripts/statusline.sh +4 -20
  80. package/scripts/telemetry-analytics.js +0 -357
  81. package/scripts/thompson-sampling.js +10 -31
  82. package/scripts/thumbgate-bench.js +1 -16
  83. package/scripts/thumbgate-search.js +19 -85
  84. package/scripts/tool-registry.js +1 -169
  85. package/scripts/vector-store.js +0 -45
  86. package/scripts/workflow-sentinel.js +53 -286
  87. package/scripts/workspace-evolver.js +2 -62
  88. package/src/api/server.js +290 -2578
  89. package/.claude/commands/dashboard.md +0 -15
  90. package/.claude/commands/thumbgate-blocked.md +0 -27
  91. package/.claude/commands/thumbgate-dashboard.md +0 -15
  92. package/.claude/commands/thumbgate-doctor.md +0 -30
  93. package/.claude/commands/thumbgate-guard.md +0 -36
  94. package/.claude/commands/thumbgate-protect.md +0 -30
  95. package/.claude/commands/thumbgate-rules.md +0 -30
  96. package/.well-known/agentic-verify.txt +0 -1
  97. package/adapters/gcp/dfcx-webhook-gate.js +0 -295
  98. package/adapters/letta/README.md +0 -41
  99. package/adapters/letta/thumbgate-letta-adapter.js +0 -133
  100. package/adapters/policy-engine/ethicore-guardian-client.js +0 -68
  101. package/adapters/policy-engine/thumbgate-policy-engine-adapter.js +0 -260
  102. package/bench/observability-eval-suite.json +0 -26
  103. package/bin/dashboard-cli.js +0 -7
  104. package/commands/dashboard.md +0 -15
  105. package/commands/thumbgate-dashboard.md +0 -15
  106. package/config/builtin-lessons.json +0 -23
  107. package/config/gate-classifier-routing.json +0 -98
  108. package/config/post-deploy-marketing-pages.json +0 -61
  109. package/public/about.html +0 -162
  110. package/public/agent-manager.html +0 -179
  111. package/public/agents-cost-savings.html +0 -153
  112. package/public/ai-malpractice-prevention.html +0 -818
  113. package/public/assets/brand/github-social-preview.png +0 -0
  114. package/public/assets/brand/thumbgate-icon-512.png +0 -0
  115. package/public/assets/brand/thumbgate-icon-pro-512.png +0 -0
  116. package/public/assets/brand/thumbgate-icon-team-512.png +0 -0
  117. package/public/assets/brand/thumbgate-logo-1200x360.png +0 -0
  118. package/public/assets/brand/thumbgate-logo-transparent.svg +0 -22
  119. package/public/assets/brand/thumbgate-mark-inline-v3.svg +0 -19
  120. package/public/assets/brand/thumbgate-mark-pro.svg +0 -23
  121. package/public/assets/brand/thumbgate-mark-team.svg +0 -26
  122. package/public/assets/brand/thumbgate-mark.svg +0 -21
  123. package/public/assets/brand/thumbgate-wordmark.svg +0 -20
  124. package/public/assets/claude-thumbgate-statusbar.svg +0 -8
  125. package/public/assets/codex-thumbgate-statusbar-test.svg +0 -9
  126. package/public/assets/legal-intake-control-flow.svg +0 -66
  127. package/public/brand/thumbgate-mark.svg +0 -19
  128. package/public/brand/thumbgate-og.svg +0 -16
  129. package/public/chatgpt-app.html +0 -330
  130. package/public/codex-enterprise.html +0 -123
  131. package/public/js/buyer-intent.js +0 -672
  132. package/public/pricing.html +0 -399
  133. package/scripts/action-receipts.js +0 -324
  134. package/scripts/activation-quickstart.js +0 -187
  135. package/scripts/agent-operations-planner.js +0 -621
  136. package/scripts/ai-component-inventory.js +0 -367
  137. package/scripts/async-eval-observability.js +0 -236
  138. package/scripts/audit.js +0 -65
  139. package/scripts/aws-blocks-guardrails.js +0 -228
  140. package/scripts/classifier-routing.js +0 -130
  141. package/scripts/dashboard-chat.js +0 -332
  142. package/scripts/feedback-aggregate.js +0 -281
  143. package/scripts/feedback-sanitizer.js +0 -105
  144. package/scripts/hob-pack.js +0 -591
  145. package/scripts/hook-stop-anti-claim.js +0 -301
  146. package/scripts/install-shim.js +0 -87
  147. package/scripts/mcp-oauth.js +0 -293
  148. package/scripts/noop-detect.js +0 -285
  149. package/scripts/omlx-smoke.js +0 -192
  150. package/scripts/parallel-workflow-orchestrator.js +0 -293
  151. package/scripts/plan-gate.js +0 -243
  152. package/scripts/plausible-domain-config.js +0 -99
  153. package/scripts/qa-scenario-planner.js +0 -136
  154. package/scripts/repeat-metric.js +0 -137
  155. package/scripts/secret-fixture-tokens.js +0 -61
  156. package/scripts/secret-redaction.js +0 -166
  157. package/scripts/self-harness-optimizer.js +0 -141
  158. package/scripts/self-healing-check.js +0 -193
  159. package/scripts/silent-failure-cluster.js +0 -531
  160. package/scripts/statusline-cache-read.js +0 -57
  161. package/scripts/sync-telemetry-from-prod.js +0 -374
  162. package/scripts/tool-contract-validator.js +0 -76
  163. package/scripts/trajectory-scorer.js +0 -63
  164. package/scripts/verify-marketing-pages-deployed.js +0 -195
  165. package/scripts/visitor-journey.js +0 -172
@@ -4,94 +4,27 @@
4
4
  const { runStep } = require('./durability/step');
5
5
 
6
6
  const MODELS = {
7
- FAST: 'glm-5.2',
8
- SMART: 'glm-5.2',
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 _anthropicClient = null;
23
- let _geminiClient = null;
15
+ let _client = null;
24
16
 
25
- function getZaiApiKey(env = process.env) {
26
- return env.ZAI_API_KEY || env.THUMBGATE_ZAI_API_KEY || env.GLM_API_KEY || '';
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 (_anthropicClient) return _anthropicClient;
90
- if (!isAvailable('anthropic')) return null;
22
+ if (_client) return _client;
23
+ if (!isAvailable()) return null;
91
24
  try {
92
25
  const Anthropic = require('@anthropic-ai/sdk');
93
- _anthropicClient = new Anthropic();
94
- return _anthropicClient;
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' || explicit === 'vertex') return explicit;
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 === 'vertex'
154
- ? 'vertex-api'
155
- : providerMode === 'managed'
156
- ? 'managed-api'
157
- : supportsSparseAttention
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 === 'vertex') {
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 providerMode = resolveProviderMode(e);
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,