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,7 +4,6 @@
4
4
  const fs = require('node:fs');
5
5
  const os = require('node:os');
6
6
  const path = require('node:path');
7
- const { expandFixturePlaceholders } = require('./secret-fixture-tokens');
8
7
 
9
8
  const ROOT = path.join(__dirname, '..');
10
9
  const DEFAULT_SUITE_PATH = path.join(ROOT, 'bench', 'thumbgate-bench.json');
@@ -181,20 +180,6 @@ function assertObject(value, label) {
181
180
  }
182
181
  }
183
182
 
184
- function expandScenarioFixturePlaceholders(value) {
185
- if (typeof value === 'string') return expandFixturePlaceholders(value);
186
- if (Array.isArray(value)) return value.map(expandScenarioFixturePlaceholders);
187
- if (value && typeof value === 'object') {
188
- return Object.fromEntries(
189
- Object.entries(value).map(([key, nestedValue]) => [
190
- key,
191
- expandScenarioFixturePlaceholders(nestedValue),
192
- ]),
193
- );
194
- }
195
- return value;
196
- }
197
-
198
183
  function loadScenarioSuite(filePath = DEFAULT_SUITE_PATH) {
199
184
  const suite = readJson(filePath);
200
185
  assertObject(suite, 'Scenario suite');
@@ -217,7 +202,7 @@ function loadScenarioSuite(filePath = DEFAULT_SUITE_PATH) {
217
202
  throw new Error(`Scenario ${id} has invalid expectedDecision`);
218
203
  }
219
204
  return {
220
- ...expandScenarioFixturePlaceholders(scenario),
205
+ ...scenario,
221
206
  id,
222
207
  unsafe: Boolean(scenario.unsafe),
223
208
  positivePattern: Boolean(scenario.positivePattern),
@@ -151,67 +151,8 @@ function sortResults(results) {
151
151
  });
152
152
  }
153
153
 
154
- function extractFeedbackId(str) {
155
- if (!str) return null;
156
- const match = str.match(/fb[_-]\d+[_-][a-z0-9]+/i);
157
- return match ? match[0].replace(/-/g, '_').toLowerCase() : null;
158
- }
159
-
160
- function deduplicateResults(results) {
161
- const bestByFeedbackId = new Map();
162
-
163
- for (const r of results) {
164
- const feedId = extractFeedbackId(r.id || r.title || r.file || '');
165
- if (feedId) {
166
- const existing = bestByFeedbackId.get(feedId);
167
- if (!existing) {
168
- bestByFeedbackId.set(feedId, r);
169
- } else {
170
- const sourceOrder = { feedback: 4, contextfs: 3, prevention_rule: 2, document: 1 };
171
- const existingOrder = sourceOrder[existing.source] || 0;
172
- const currentOrder = sourceOrder[r.source] || 0;
173
- if (currentOrder > existingOrder) {
174
- bestByFeedbackId.set(feedId, r);
175
- } else if (currentOrder === existingOrder && (r.score || 0) > (existing.score || 0)) {
176
- bestByFeedbackId.set(feedId, r);
177
- }
178
- }
179
- }
180
- }
181
-
182
- const finalResults = [];
183
- const seenContent = new Set();
184
- const seenIds = new Set();
185
-
186
- for (const r of results) {
187
- const feedId = extractFeedbackId(r.id || r.title || r.file || '');
188
- let recordToUse = r;
189
- if (feedId) {
190
- recordToUse = bestByFeedbackId.get(feedId);
191
- if (seenIds.has(recordToUse.id)) continue;
192
- } else {
193
- if (r.id && r.id !== 'null' && String(r.id).trim() !== '') {
194
- if (seenIds.has(r.id)) continue;
195
- }
196
- }
197
-
198
- const normTitle = String(recordToUse.title || '').trim().toLowerCase();
199
- const normContext = String(recordToUse.context || '').trim().toLowerCase();
200
- const contentKey = `${normTitle}|${normContext}`;
201
- if (seenContent.has(contentKey)) continue;
202
-
203
- if (recordToUse.id && recordToUse.id !== 'null' && String(recordToUse.id).trim() !== '') {
204
- seenIds.add(recordToUse.id);
205
- }
206
- seenContent.add(contentKey);
207
- finalResults.push(recordToUse);
208
- }
209
-
210
- return finalResults;
211
- }
212
-
213
- function getFeedbackResults(query, limit, signal, feedbackDir) {
214
- const results = searchFeedbackLog(query, Math.max(limit * 3, limit), { feedbackDir });
154
+ function getFeedbackResults(query, limit, signal) {
155
+ const results = searchFeedbackLog(query, Math.max(limit * 3, limit));
215
156
  const normalizedSignal = normalizeSignal(signal);
216
157
  const filtered = normalizedSignal
217
158
  ? results.filter((record) => normalizeRecordSignal(record.signal) === normalizedSignal)
@@ -219,19 +160,19 @@ function getFeedbackResults(query, limit, signal, feedbackDir) {
219
160
  return filtered.slice(0, limit).map(mapFeedbackResult);
220
161
  }
221
162
 
222
- function getContextResults(query, limit, feedbackDir) {
223
- return searchContextFs(query, limit, { feedbackDir }).map(mapContextResult);
163
+ function getContextResults(query, limit) {
164
+ return searchContextFs(query, limit).map(mapContextResult);
224
165
  }
225
166
 
226
- function getRuleResults(query, limit, feedbackDir) {
227
- return searchPreventionRulesSync(query, limit, { feedbackDir }).map(mapRuleResult);
167
+ function getRuleResults(query, limit) {
168
+ return searchPreventionRulesSync(query, limit).map(mapRuleResult);
228
169
  }
229
170
 
230
- function getDocumentResults(query, limit, feedbackDir) {
231
- return searchImportedDocuments({ query, limit, feedbackDir }).map(mapDocumentResult);
171
+ function getDocumentResults(query, limit) {
172
+ return searchImportedDocuments({ query, limit }).map(mapDocumentResult);
232
173
  }
233
174
 
234
- function searchThumbgate({ query, source = 'all', limit = 10, signal = null, feedbackDir = null } = {}) {
175
+ function searchThumbgate({ query, source = 'all', limit = 10, signal = null } = {}) {
235
176
  const trimmedQuery = String(query || '').trim();
236
177
  if (!trimmedQuery) {
237
178
  throw new Error('query is required');
@@ -241,29 +182,22 @@ function searchThumbgate({ query, source = 'all', limit = 10, signal = null, fee
241
182
  const normalizedSignal = normalizeSignal(signal);
242
183
  const normalizedLimit = normalizeLimit(limit);
243
184
 
244
- const fetchLimit = Math.max(100, normalizedLimit * 5);
245
-
246
185
  let results = [];
247
186
  if (normalizedSource === 'feedback') {
248
- const raw = getFeedbackResults(trimmedQuery, fetchLimit, normalizedSignal, feedbackDir);
249
- results = deduplicateResults(raw).slice(0, normalizedLimit);
187
+ results = getFeedbackResults(trimmedQuery, normalizedLimit, normalizedSignal);
250
188
  } else if (normalizedSource === 'context') {
251
- const raw = getContextResults(trimmedQuery, fetchLimit, feedbackDir);
252
- results = deduplicateResults(raw).slice(0, normalizedLimit);
189
+ results = getContextResults(trimmedQuery, normalizedLimit);
253
190
  } else if (normalizedSource === 'rules') {
254
- const raw = getRuleResults(trimmedQuery, fetchLimit, feedbackDir);
255
- results = deduplicateResults(raw).slice(0, normalizedLimit);
191
+ results = getRuleResults(trimmedQuery, normalizedLimit);
256
192
  } else if (normalizedSource === 'documents') {
257
- const raw = getDocumentResults(trimmedQuery, fetchLimit, feedbackDir);
258
- results = deduplicateResults(raw).slice(0, normalizedLimit);
193
+ results = getDocumentResults(trimmedQuery, normalizedLimit);
259
194
  } else {
260
- const combined = [
261
- ...getFeedbackResults(trimmedQuery, fetchLimit, normalizedSignal, feedbackDir),
262
- ...getContextResults(trimmedQuery, fetchLimit, feedbackDir),
263
- ...getRuleResults(trimmedQuery, fetchLimit, feedbackDir),
264
- ...getDocumentResults(trimmedQuery, fetchLimit, feedbackDir),
265
- ];
266
- results = deduplicateResults(sortResults(combined)).slice(0, normalizedLimit);
195
+ results = sortResults([
196
+ ...getFeedbackResults(trimmedQuery, normalizedLimit, normalizedSignal),
197
+ ...getContextResults(trimmedQuery, normalizedLimit),
198
+ ...getRuleResults(trimmedQuery, normalizedLimit),
199
+ ...getDocumentResults(trimmedQuery, normalizedLimit),
200
+ ]).slice(0, normalizedLimit);
267
201
  }
268
202
 
269
203
  return {
@@ -1,23 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- // Human-readable display title from a snake_case tool name.
5
- // e.g. "capture_feedback" -> "Capture Feedback". The Claude Connectors Directory
6
- // requires every tool to carry BOTH a title and a readOnlyHint/destructiveHint.
7
- function humanizeTitle(name) {
8
- return String(name || '')
9
- .split(/[_-]+/)
10
- .filter(Boolean)
11
- .map((w) => w.charAt(0).toUpperCase() + w.slice(1))
12
- .join(' ');
13
- }
14
-
15
4
  function readOnlyTool(tool) {
16
5
  return {
17
6
  ...tool,
18
- title: tool.title || humanizeTitle(tool.name),
19
7
  annotations: {
20
- title: tool.title || humanizeTitle(tool.name),
21
8
  readOnlyHint: true,
22
9
  },
23
10
  };
@@ -26,40 +13,12 @@ function readOnlyTool(tool) {
26
13
  function destructiveTool(tool) {
27
14
  return {
28
15
  ...tool,
29
- title: tool.title || humanizeTitle(tool.name),
30
16
  annotations: {
31
- title: tool.title || humanizeTitle(tool.name),
32
17
  destructiveHint: true,
33
18
  },
34
19
  };
35
20
  }
36
21
 
37
- const GOAL_CONTRACT_SCHEMA = {
38
- type: 'object',
39
- description: 'Optional agent handoff contract. Use this when a worker/orchestrator/reviewer loop needs explicit done criteria before a done/fixed/shipped claim is allowed.',
40
- properties: {
41
- goal: { type: 'string', description: 'The operator-visible goal this claim is trying to close.' },
42
- doneWhen: {
43
- type: 'array',
44
- items: { type: 'string' },
45
- description: 'Human-readable acceptance criteria for the task.',
46
- },
47
- proveBy: {
48
- type: 'array',
49
- items: { type: 'string' },
50
- description: 'Tracked action ids required before the claim can pass, such as tests_passed, review_completed, ci_green, deploy_verified, or operator_approved.',
51
- },
52
- mustNotChange: {
53
- type: 'array',
54
- items: { type: 'string' },
55
- description: 'Protected areas or constraints the agents must preserve while completing the goal.',
56
- },
57
- workerAgent: { type: 'string', description: 'Agent responsible for implementation.' },
58
- reviewerAgent: { type: 'string', description: 'Agent responsible for independent verification.' },
59
- orchestratorAgent: { type: 'string', description: 'Agent responsible for routing and deciding whether done can be claimed.' },
60
- },
61
- };
62
-
63
22
  const TOOLS = [
64
23
  readOnlyTool({
65
24
  name: 'capture_feedback',
@@ -161,19 +120,6 @@ const TOOLS = [
161
120
  required: ['toolName'],
162
121
  },
163
122
  }),
164
- readOnlyTool({
165
- name: 'ai_component_inventory',
166
- description: 'Scan a project for AI/ML provider SDKs, agent frameworks, vector databases, Vertex/Gemini/Dialogflow CX usage, and model artifacts. Returns evidence suitable for enterprise AI inventory and ML-BOM review.',
167
- inputSchema: {
168
- type: 'object',
169
- properties: {
170
- rootDir: { type: 'string', description: 'Project root to scan. Defaults to the current process working directory.' },
171
- format: { type: 'string', enum: ['summary', 'json', 'cyclonedx'], description: 'Response format. summary is compact text; json returns ThumbGate inventory; cyclonedx returns ML-BOM JSON.' },
172
- maxFiles: { type: 'number', description: 'Maximum files to scan (default 2500).' },
173
- includeSnippets: { type: 'boolean', description: 'Include matched source snippets in evidence. Defaults true.' },
174
- },
175
- },
176
- }),
177
123
  readOnlyTool({
178
124
  name: 'search_thumbgate',
179
125
  description: 'Search raw ThumbGate state across feedback logs, ContextFS memory, prevention rules, and imported policy documents.',
@@ -425,24 +371,6 @@ const TOOLS = [
425
371
  },
426
372
  },
427
373
  }),
428
- readOnlyTool({
429
- name: 'suggest_fix',
430
- description: 'Suggest corrective actions for a described failure by searching the lesson DB and prevention rules. Returns up to 3 ranked suggestions with their source. Call this when something goes wrong and you need guidance on what to do next.',
431
- inputSchema: {
432
- type: 'object',
433
- required: ['context'],
434
- properties: {
435
- context: {
436
- type: 'string',
437
- description: 'Description of what went wrong or what the agent is trying to fix.',
438
- },
439
- limit: {
440
- type: 'number',
441
- description: 'Maximum number of suggestions to return (default 3, max 5).',
442
- },
443
- },
444
- },
445
- }),
446
374
  readOnlyTool({
447
375
  name: 'infer_lesson_from_history',
448
376
  description: 'Perform autonomous inference on chat history to identify why a failure occurred and what rule should be recorded.',
@@ -489,7 +417,6 @@ const TOOLS = [
489
417
  bundleId: { type: 'string' },
490
418
  partnerProfile: { type: 'string' },
491
419
  delegationMode: { type: 'string', enum: ['off', 'auto', 'sequential'] },
492
- enforcePlanQuality: { type: 'boolean' },
493
420
  approved: { type: 'boolean' },
494
421
  repoPath: { type: 'string' },
495
422
  },
@@ -831,17 +758,6 @@ const TOOLS = [
831
758
  items: { type: 'string' },
832
759
  description: 'Optional protected-file globs that require explicit approval before editing or publishing',
833
760
  },
834
- workflowContract: {
835
- type: 'object',
836
- description: 'Optional deterministic workflow run contract. Supports workflowId, allowedBranches, blockedActions, requiredEvidence, and completionGate.',
837
- properties: {
838
- workflowId: { type: 'string' },
839
- allowedBranches: { type: 'array', items: { type: 'string' } },
840
- blockedActions: { type: 'array', items: { type: 'string' } },
841
- requiredEvidence: { type: 'array', items: { type: 'string' } },
842
- completionGate: { type: 'string' },
843
- },
844
- },
845
761
  repoPath: { type: 'string', description: 'Optional repo root used when evaluating git diff scope' },
846
762
  localOnly: { type: 'boolean', description: 'When true, also marks the task as local-only' },
847
763
  clear: { type: 'boolean', description: 'Clear the current task scope instead of setting one' },
@@ -919,56 +835,6 @@ const TOOLS = [
919
835
  },
920
836
  },
921
837
  }),
922
- destructiveTool({
923
- name: 'detect_noop',
924
- title: 'Detect No-op Action',
925
- description: 'Detect whether a tool call was a no-op (state unchanged) or identical to a prior attempt in the session — a cheap repeat-loop signal. Records the action attempt state for repeat detection.',
926
- inputSchema: {
927
- type: 'object',
928
- required: ['actionId'],
929
- properties: {
930
- actionId: { type: 'string', description: 'Stable identifier for the action being checked (e.g. the file path or command being attempted)' },
931
- kind: { type: 'string', enum: ['file', 'command'], description: 'Action kind: file edit/write or command execution' },
932
- filePath: { type: 'string', description: 'Path of the file the action targets (file kind)' },
933
- beforeContent: { type: 'string', description: 'File content before the action (file kind)' },
934
- afterContent: { type: 'string', description: 'File content after the action (file kind)' },
935
- exitCode: { type: 'number', description: 'Command exit code (command kind)' },
936
- stdout: { type: 'string', description: 'Command stdout (command kind)' },
937
- stderr: { type: 'string', description: 'Command stderr (command kind)' },
938
- sessionId: { type: 'string', description: 'Optional session id used to scope repeat-attempt detection' },
939
- },
940
- },
941
- }),
942
- destructiveTool({
943
- name: 'record_action_receipt',
944
- title: 'Record Action Receipt',
945
- description: 'Pair a tracked tool call with its outcome (diff, exit code, test result) so a promoted lesson encodes "this action -> this outcome", not just a thumbs signal. Appends to the action-receipts log.',
946
- inputSchema: {
947
- type: 'object',
948
- required: ['actionId'],
949
- properties: {
950
- actionId: { type: 'string', description: 'Identifier of the tracked action this receipt pairs with' },
951
- toolName: { type: 'string', description: 'Name of the tool that was invoked' },
952
- toolInput: { type: 'object', description: 'Structured input the tool was called with' },
953
- diff: { type: 'string', description: 'Optional unified diff or change summary produced by the action' },
954
- exitCode: { type: 'number', description: 'Optional command exit code outcome' },
955
- testOutcome: { type: 'string', description: 'Optional test outcome (e.g. passed, failed, 12/12)' },
956
- stateHash: { type: 'string', description: 'Optional post-action state hash (from detect_noop)' },
957
- },
958
- },
959
- }),
960
- readOnlyTool({
961
- name: 'get_action_receipts',
962
- title: 'Get Action Receipts',
963
- description: 'Read outcome-paired action receipts. Returns the receipt for a specific actionId, or the most recent receipts when no actionId is given.',
964
- inputSchema: {
965
- type: 'object',
966
- properties: {
967
- actionId: { type: 'string', description: 'Optional action id to fetch the matching receipt for' },
968
- limit: { type: 'number', description: 'Max number of recent receipts to return when no actionId is given (default 20)' },
969
- },
970
- },
971
- }),
972
838
  readOnlyTool({
973
839
  name: 'verify_claim',
974
840
  description: 'Check whether a claim has enough tracked evidence before the agent asserts it.',
@@ -977,7 +843,6 @@ const TOOLS = [
977
843
  required: ['claim'],
978
844
  properties: {
979
845
  claim: { type: 'string', description: 'The claim text to verify' },
980
- goalContract: GOAL_CONTRACT_SCHEMA,
981
846
  },
982
847
  },
983
848
  }),
@@ -1426,7 +1291,6 @@ const TOOLS = [
1426
1291
  claim: { type: 'string', description: 'The completion claim text to verify (e.g. "Fix shipped", "Tests passing")' },
1427
1292
  mode: { type: 'string', enum: ['blocking', 'advisory'], description: 'blocking (default) returns blocking=true when evidence missing; advisory returns blocking=false' },
1428
1293
  sessionId: { type: 'string', description: 'Optional session id to associate with the gate decision' },
1429
- goalContract: GOAL_CONTRACT_SCHEMA,
1430
1294
  },
1431
1295
  },
1432
1296
  }),
@@ -1484,40 +1348,8 @@ const TOOLS = [
1484
1348
  },
1485
1349
  },
1486
1350
  }),
1487
- destructiveTool({
1488
- name: 'parallel_workflow',
1489
- description: 'Execute a parallel, multi-step subtask workflow to resolve an objective like a security audit, performance benchmark, or repository inspection.',
1490
- inputSchema: {
1491
- type: 'object',
1492
- required: ['objective'],
1493
- properties: {
1494
- objective: { type: 'string', description: 'The objective to plan and execute (e.g. security audit, performance benchmark)' },
1495
- concurrency: { type: 'number', description: 'Maximum parallel subtasks (default 3)' },
1496
- timeoutMs: { type: 'number', description: 'Timeout in milliseconds (default 60000)' },
1497
- },
1498
- },
1499
- }),
1500
1351
  ];
1501
1352
 
1502
- // Normalize at export: guarantee EVERY tool carries a human-readable title and a
1503
- // readOnlyHint/destructiveHint annotation (both required by the Connectors
1504
- // Directory; the #1 rejection cause is missing annotations). Tools defined as
1505
- // plain objects (not via readOnlyTool/destructiveTool) are backfilled here:
1506
- // title from the name, and a conservative destructiveHint when no hint is set
1507
- // (so an un-hinted tool is gated rather than silently treated as read-only).
1508
- const NORMALIZED_TOOLS = TOOLS.map((tool) => {
1509
- const title = tool.title || humanizeTitle(tool.name);
1510
- const existing = tool.annotations || {};
1511
- const hasHint = existing.readOnlyHint === true || existing.destructiveHint === true;
1512
- const annotations = {
1513
- title,
1514
- ...existing,
1515
- ...(hasHint ? {} : { destructiveHint: true }),
1516
- };
1517
- return { ...tool, title, annotations };
1518
- });
1519
-
1520
1353
  module.exports = {
1521
- TOOLS: NORMALIZED_TOOLS,
1522
- humanizeTitle,
1354
+ TOOLS,
1523
1355
  };
@@ -172,30 +172,6 @@ async function embedWithGemini(text, options = {}) {
172
172
  return values.map(Number);
173
173
  }
174
174
 
175
- async function embedWithCoreAI(text, options = {}) {
176
- if (process.platform !== 'darwin') {
177
- throw new Error('Core AI is only supported on macOS');
178
- }
179
- const endpoint = process.env.THUMBGATE_COREAI_ENDPOINT || 'http://localhost:8088';
180
- try {
181
- const res = await fetch(`${endpoint}/embed`, {
182
- method: 'POST',
183
- headers: { 'Content-Type': 'application/json' },
184
- body: JSON.stringify({ text, options }),
185
- signal: AbortSignal.timeout(2000),
186
- });
187
- if (res.ok) {
188
- const payload = await res.json();
189
- if (Array.isArray(payload.embedding)) {
190
- return payload.embedding.map(Number);
191
- }
192
- }
193
- } catch (err) {
194
- throw new Error(`Core AI local service unavailable: ${err.message}`);
195
- }
196
- throw new Error('Core AI local service did not return a valid embedding');
197
- }
198
-
199
175
  async function embed(text, options = {}) {
200
176
  if (process.env.THUMBGATE_VECTOR_STUB_EMBED === 'true') {
201
177
  // Deterministic 384-dim unit vector: first element = 1.0, rest = 0.0
@@ -204,26 +180,6 @@ async function embed(text, options = {}) {
204
180
  return stub;
205
181
  }
206
182
  const geminiConfig = resolveGeminiEmbeddingConfig();
207
- if (geminiConfig.provider === 'coreai') {
208
- try {
209
- const vector = await embedWithCoreAI(text, options);
210
- _lastEmbeddingProfile = {
211
- generatedAt: new Date().toISOString(),
212
- source: 'local-coreai',
213
- activeProfile: {
214
- id: 'coreai',
215
- model: 'Core AI local model',
216
- outputDimensionality: vector.length,
217
- task: options.task || 'code retrieval',
218
- rationale: 'Local Core AI Apple Silicon accelerated path.',
219
- },
220
- fallbackUsed: false,
221
- };
222
- return vector;
223
- } catch (coreaiError) {
224
- console.warn(`Core AI embedding failed, falling back to local: ${coreaiError.message}`);
225
- }
226
- }
227
183
  if (geminiConfig.enabled) {
228
184
  try {
229
185
  const vector = await embedWithGemini(text, options);
@@ -356,7 +312,6 @@ function setGeminiEmbedderForTests(loader) {
356
312
  module.exports = {
357
313
  upsertFeedback,
358
314
  searchSimilar,
359
- embed,
360
315
  TABLE_NAME,
361
316
  getEmbeddingConfig,
362
317
  getLastEmbeddingProfile,