thumbgate 1.35.0 → 1.37.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/.agents/skills/cyberstrike-compare-not-clone/SKILL.md +36 -0
  2. package/.agents/skills/gitlab-sandbox-allowlist-not-trust/SKILL.md +77 -0
  3. package/.agents/skills/jit-harness-compare-not-clone/SKILL.md +34 -0
  4. package/.agents/skills/openui-catalog-compose-honesty/SKILL.md +64 -0
  5. package/.agents/skills/zvec-grep-compare-not-clone/SKILL.md +34 -0
  6. package/.claude-plugin/plugin.json +1 -1
  7. package/.well-known/llms.txt +1 -0
  8. package/.well-known/mcp/server-card.json +1 -1
  9. package/CONTRIBUTING.md +95 -0
  10. package/README.md +195 -632
  11. package/THIRD_PARTY_NOTICES.md +89 -0
  12. package/adapters/claude/.mcp.json +2 -2
  13. package/adapters/forge/forge.yaml +3 -3
  14. package/adapters/future-agi/.mcp.json +8 -0
  15. package/adapters/future-agi/FUTURE_AGI.md +23 -0
  16. package/adapters/future-agi/config.toml +3 -0
  17. package/adapters/future-agi/future-agi-bridge.js +9 -0
  18. package/adapters/future-agi/opencode.json +8 -0
  19. package/adapters/herdr/herdr-plugin.toml +18 -0
  20. package/adapters/mcp/server-stdio.js +238 -25
  21. package/adapters/opencode/opencode.json +1 -1
  22. package/adapters/workos/WORKOS.md +52 -0
  23. package/bin/cli.js +336 -3
  24. package/bin/futureagi-bridge +9 -0
  25. package/config/gate-templates.json +581 -4
  26. package/config/gates/actor-critic-audit.json +34 -0
  27. package/config/gates/default.json +9 -3
  28. package/config/gates/five-walls-governance.json +34 -0
  29. package/config/gates/future-agi-guardrails.json +34 -0
  30. package/config/gates/radware-threat-defense-2026.json +61 -0
  31. package/config/gates/simatree-data-governance.json +33 -0
  32. package/config/mcp-allowlists.json +4 -0
  33. package/config/merge-quality-checks.json +6 -0
  34. package/config/model-candidates.json +312 -29
  35. package/config/model-tiers.json +18 -0
  36. package/config/post-deploy-marketing-pages.json +10 -0
  37. package/config/progressive/01-wire-only.json +11 -0
  38. package/config/progressive/02-dashboard-empty-ok.json +10 -0
  39. package/config/progressive/03-one-lesson.json +10 -0
  40. package/config/progressive/04-warn-fires.json +11 -0
  41. package/config/progressive/05-strict-optional.json +11 -0
  42. package/config/progressive/README.md +15 -0
  43. package/config/schemas/broker-execution-receipt.schema.json +139 -0
  44. package/config/schemas/provider-execution-attestation-v1.schema.json +58 -0
  45. package/conformance/provider-attestation/vectors.json +320 -0
  46. package/docs/specs/provider-execution-attestation-v1.md +69 -0
  47. package/openapi/openapi.yaml +15 -0
  48. package/package.json +390 -148
  49. package/public/about.html +2 -2
  50. package/public/ai-malpractice-prevention.html +7 -7
  51. package/public/blog/a-10-dollar-vps-is-not-a-computer.html +143 -0
  52. package/public/blog/a-receipt-is-not-world-state.html +388 -0
  53. package/public/blog/git-at-agent-scale.html +374 -0
  54. package/public/blog/no-llm-in-the-gate.html +133 -0
  55. package/public/blog.html +80 -0
  56. package/public/case-studies.html +16 -1
  57. package/public/compare.html +28 -0
  58. package/public/diagnostic.html +216 -7
  59. package/public/docs/connectors.html +39 -0
  60. package/public/federal.html +2 -2
  61. package/public/founders.html +639 -0
  62. package/public/index.html +87 -9
  63. package/public/install.html +8 -8
  64. package/public/learn.html +39 -0
  65. package/public/numbers.html +2 -2
  66. package/public/peter.html +310 -0
  67. package/public/platform-partners.html +119 -0
  68. package/public/pricing.html +24 -3
  69. package/public/privacy.html +117 -0
  70. package/public/pro.html +17 -0
  71. package/public/support.html +62 -0
  72. package/public/terms.html +130 -0
  73. package/public/third-party-notices.html +95 -0
  74. package/public/yt.html +351 -0
  75. package/scripts/action-receipts.js +133 -3
  76. package/scripts/adaptive-governance-arena.js +349 -0
  77. package/scripts/admin-override.js +205 -0
  78. package/scripts/agent-action-inventory.js +869 -0
  79. package/scripts/agent-audit-trace.js +42 -2
  80. package/scripts/agent-egress-policy.js +1117 -0
  81. package/scripts/agent-memory-lifecycle.js +141 -2
  82. package/scripts/agent-operations-planner.js +441 -1
  83. package/scripts/agent-readiness.js +68 -0
  84. package/scripts/agent-security-central.js +647 -0
  85. package/scripts/allowlist-bridge-honesty.js +417 -0
  86. package/scripts/async-job-runner.js +102 -11
  87. package/scripts/audit-trail.js +212 -0
  88. package/scripts/billing.js +1 -1
  89. package/scripts/broker-execution-receipts.js +719 -0
  90. package/scripts/budget-aware-gates-proof.js +423 -0
  91. package/scripts/claude-feedback-sync.js +29 -3
  92. package/scripts/claw-harness-production.js +237 -0
  93. package/scripts/cli-schema.js +163 -1
  94. package/scripts/codex-runbook-flywheel.js +318 -0
  95. package/scripts/context-footprint.js +186 -0
  96. package/scripts/contextfs.js +143 -61
  97. package/scripts/dashboard.js +251 -32
  98. package/scripts/deepseek-v4-runtime-guardrails.js +72 -6
  99. package/scripts/docker-sandbox-planner.js +18 -0
  100. package/scripts/double-blind-eval-protocol.js +252 -0
  101. package/scripts/edotenv-rl-gateway.js +259 -0
  102. package/scripts/ensure-production-search-corpus.js +162 -0
  103. package/scripts/eval-holdout.js +311 -0
  104. package/scripts/feedback-aggregate.js +21 -2
  105. package/scripts/feedback-loop.js +87 -5
  106. package/scripts/feedback-quality.js +9 -0
  107. package/scripts/file-ledger-lock.js +4 -1
  108. package/scripts/financial-control-plane.js +41 -1
  109. package/scripts/find-dormant-requires.js +118 -0
  110. package/scripts/fs-utils.js +84 -8
  111. package/scripts/gates-engine.js +810 -63
  112. package/scripts/generate-case-study-outreach.js +24 -15
  113. package/scripts/git-at-scale.js +628 -0
  114. package/scripts/governance-conflict-audit.js +1650 -0
  115. package/scripts/governance-difficulty-curriculum.js +328 -0
  116. package/scripts/graphrag-retrieval.js +275 -0
  117. package/scripts/gurobi-optimizer.js +324 -0
  118. package/scripts/gurobi_optimizer.py +485 -0
  119. package/scripts/harness-selector.js +82 -1
  120. package/scripts/hidden-entry-points.js +284 -0
  121. package/scripts/human-escalation.js +199 -1
  122. package/scripts/hybrid-feedback-context.js +152 -19
  123. package/scripts/intent-governed-execution.js +602 -0
  124. package/scripts/intervention-policy.js +123 -20
  125. package/scripts/jit-harness-compose.js +628 -0
  126. package/scripts/jsonl-watcher.js +10 -0
  127. package/scripts/lesson-embedding-index.js +95 -12
  128. package/scripts/lesson-retrieval.js +105 -19
  129. package/scripts/local-model-profile.js +19 -2
  130. package/scripts/mailer/resend-mailer.js +1 -1
  131. package/scripts/matryoshka-embedding.js +235 -0
  132. package/scripts/mcp-oauth.js +42 -4
  133. package/scripts/mcp-session-handles.js +1016 -0
  134. package/scripts/mcp-wiring-doctor.js +314 -0
  135. package/scripts/memory-firewall.js +115 -2
  136. package/scripts/memory-scope-readiness.js +299 -0
  137. package/scripts/memory-vs-rag-route.js +161 -0
  138. package/scripts/model-tier-router.js +148 -21
  139. package/scripts/nvidia-specdecode-al-doctor.js +536 -0
  140. package/scripts/openui-catalog-compose-honesty.js +593 -0
  141. package/scripts/operational-integrity.js +19 -1
  142. package/scripts/override-audit.js +213 -0
  143. package/scripts/package-manager-honesty-doctor.js +458 -0
  144. package/scripts/pr-manager.js +63 -1
  145. package/scripts/prove-herdr-adapter.js +52 -0
  146. package/scripts/prove-memory-pyramid-and-symbolic-canvas.js +95 -0
  147. package/scripts/prove-workos.js +73 -0
  148. package/scripts/provider-attestation-conformance.js +192 -0
  149. package/scripts/provider-receipt-contract.js +136 -0
  150. package/scripts/qwen38-max-cost-optimizer.js +401 -0
  151. package/scripts/radware-threat-defense.js +280 -0
  152. package/scripts/rag-embedding-identity.js +221 -0
  153. package/scripts/rag-precision-guardrails.js +112 -2
  154. package/scripts/remote-feedback-capture.js +159 -0
  155. package/scripts/research-agent-harness.js +256 -0
  156. package/scripts/rsi-safety-hillclimb.js +200 -0
  157. package/scripts/rule-sprawl.js +188 -0
  158. package/scripts/schedule-manager.js +147 -0
  159. package/scripts/self-heal.js +8 -0
  160. package/scripts/session-lease.js +415 -0
  161. package/scripts/simatree-data-governance.js +347 -0
  162. package/scripts/slo-alert-engine.js +172 -7
  163. package/scripts/solver-parity.js +539 -0
  164. package/scripts/stealth-memory-injection-gate.js +333 -0
  165. package/scripts/switchyard-router.js +366 -0
  166. package/scripts/telemetry-analytics.js +84 -27
  167. package/scripts/temporal-decay-weighting.js +138 -0
  168. package/scripts/test-all.js +165 -0
  169. package/scripts/token-savings.js +42 -0
  170. package/scripts/tool-kpi-tracker.js +108 -5
  171. package/scripts/tool-registry.js +193 -5
  172. package/scripts/universal-claim-evaluator.js +14 -2
  173. package/scripts/vector-store.js +279 -9
  174. package/scripts/workflow-notebook.js +391 -0
  175. package/scripts/workflow-sentinel.js +111 -12
  176. package/scripts/workos-production-guard.js +260 -0
  177. package/scripts/workspace-search-route.js +515 -0
  178. package/server.json +2 -2
  179. package/src/agent-identity-boundary.js +76 -0
  180. package/src/agent-retrieval-cache.js +155 -0
  181. package/src/alert-noise-ledger.js +502 -0
  182. package/src/api/server.js +724 -153
  183. package/src/git-fast-cache.js +220 -0
  184. package/src/git-wal-sync.js +156 -0
  185. package/src/hash-anchored-edit.js +82 -0
  186. package/src/hermes-platform-protocol.js +475 -0
  187. package/src/hermes-sync-plane.js +241 -0
  188. package/src/index.js +30 -1
  189. package/src/iso42001-compliance-guard.js +97 -0
  190. package/src/latency-budget.js +244 -0
  191. package/src/mcp-writeguard.js +316 -0
  192. package/src/miminions-adapter.js +106 -0
  193. package/src/pipeline-compass.js +104 -0
  194. package/src/ppl-alert-pipeline.js +284 -0
  195. package/src/rendezvous-router.js +90 -0
  196. package/src/security-questionnaire.js +195 -0
package/src/index.js CHANGED
@@ -1,3 +1,32 @@
1
1
  'use strict';
2
2
 
3
- module.exports = require('../scripts/feedback-loop');
3
+ const feedbackLoop = require('../scripts/feedback-loop');
4
+ const {
5
+ HermesPlatformProtocol,
6
+ ProtocolError,
7
+ CONSEQUENTIAL_ACTIONS,
8
+ VALID_APPROVAL_SURFACES,
9
+ INVALID_APPROVAL_SURFACES,
10
+ THREAD_MODES,
11
+ FORBIDDEN_IDENTITIES,
12
+ isNamedIdentity,
13
+ } = require('./hermes-platform-protocol');
14
+ const { HermesSyncPlane } = require('./hermes-sync-plane');
15
+
16
+ // The package entry point is the production surface for `thumbgate`.
17
+ // The hosted-Hermes identity/lifecycle/approval gates are exported here so
18
+ // callers instantiate them from the public API rather than reaching into
19
+ // module paths. See docs: hosted Hermes requests route through
20
+ // HermesPlatformProtocol for turn lifecycle + approvals, and through
21
+ // HermesSyncPlane for the authorized-shape read path.
22
+ module.exports = Object.assign({}, feedbackLoop, {
23
+ HermesPlatformProtocol,
24
+ HermesSyncPlane,
25
+ ProtocolError,
26
+ CONSEQUENTIAL_ACTIONS,
27
+ VALID_APPROVAL_SURFACES,
28
+ INVALID_APPROVAL_SURFACES,
29
+ THREAD_MODES,
30
+ FORBIDDEN_IDENTITIES,
31
+ isNamedIdentity,
32
+ });
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ISO/IEC 42001:2023 Artificial Intelligence Management System (AIMS) Compliance Guard.
5
+ *
6
+ * Maps ThumbGate's pre-action gates and telemetry to formal ISO 42001 governance clauses:
7
+ * - Clause 6.1.2: AI Risk Assessment & Tiering
8
+ * - Clause 8.2: AI Impact Assessment & Tool Blast Radius Control
9
+ * - Clause 8.4: Data Lifecycle & Secret/PII Leakage Prevention
10
+ * - Clause 9.1: Continuous Telemetry & SLA/SLO Monitoring
11
+ * - Clause 10.1: Continuous Improvement via Feedback-to-Enforcement Rules
12
+ */
13
+
14
+ const ISO_42001_CLAUSES = Object.freeze({
15
+ RISK_ASSESSMENT: 'Clause 6.1.2 (AI Risk Assessment)',
16
+ IMPACT_ASSESSMENT: 'Clause 8.2 (AI Impact Assessment)',
17
+ DATA_PROTECTION: 'Clause 8.4 (Data Lifecycle & DLP)',
18
+ CONTINUOUS_MONITORING: 'Clause 9.1 (Monitoring & Telemetry)',
19
+ CORRECTIVE_ACTION: 'Clause 10.1 (Corrective Action & Rule Promotion)',
20
+ });
21
+
22
+ class ISO42001ComplianceGuard {
23
+ constructor(options = {}) {
24
+ this.options = options;
25
+ }
26
+
27
+ static evaluateToolCallCompliance(toolCall = {}) {
28
+ const findings = [];
29
+ const name = toolCall.name || toolCall.toolName || 'unknown';
30
+ const params = toolCall.parameters || toolCall.args || {};
31
+ const riskTier = toolCall.riskTier || 'minimal-impact';
32
+
33
+ // Clause 6.1.2: Risk Assessment
34
+ if (!['read-only', 'minimal-impact', 'contained-write', 'critical'].includes(riskTier)) {
35
+ findings.push({
36
+ clause: ISO_42001_CLAUSES.RISK_ASSESSMENT,
37
+ severity: 'FAIL',
38
+ message: `Unclassified risk tier '${riskTier}' on tool ${name}`,
39
+ });
40
+ }
41
+
42
+ // Clause 8.2: Blast Radius
43
+ if (riskTier === 'critical' && !toolCall.humanApproval && !toolCall.approvedByPolicy) {
44
+ findings.push({
45
+ clause: ISO_42001_CLAUSES.IMPACT_ASSESSMENT,
46
+ severity: 'FAIL',
47
+ message: `Critical tool call '${name}' requires explicit policy approval or human sign-off`,
48
+ });
49
+ }
50
+
51
+ // Clause 8.4: Data Protection / Secret Scanning
52
+ const paramStr = JSON.stringify(params);
53
+ const hasSecretPattern = /(?:ghp_[a-z0-9]{20,}|sk_live_[a-z0-9]{20,}|mock_secret_[a-z0-9]{10,}|-----BEGIN PRIVATE KEY-----)/i.test(paramStr);
54
+ if (hasSecretPattern) {
55
+ findings.push({
56
+ clause: ISO_42001_CLAUSES.DATA_PROTECTION,
57
+ severity: 'FAIL',
58
+ message: `Potential credential or secret detected in parameters for tool '${name}'`,
59
+ });
60
+ }
61
+
62
+ const compliant = findings.filter((f) => f.severity === 'FAIL').length === 0;
63
+
64
+ return {
65
+ compliant,
66
+ standard: 'ISO/IEC 42001:2023',
67
+ toolName: name,
68
+ riskTier,
69
+ findings,
70
+ timestamp: Date.now(),
71
+ };
72
+ }
73
+
74
+ static generateComplianceAuditReport(toolCalls = []) {
75
+ const results = toolCalls.map((tc) => ISO42001ComplianceGuard.evaluateToolCallCompliance(tc));
76
+ const total = results.length;
77
+ const passed = results.filter((r) => r.compliant).length;
78
+ const failed = total - passed;
79
+
80
+ return {
81
+ standard: 'ISO/IEC 42001:2023 AIMS',
82
+ auditTimestamp: new Date().toISOString(),
83
+ summary: {
84
+ totalEvaluated: total,
85
+ passed,
86
+ failed,
87
+ complianceRate: total > 0 ? (passed / total) * 100 : 100,
88
+ },
89
+ records: results,
90
+ };
91
+ }
92
+ }
93
+
94
+ module.exports = {
95
+ ISO42001ComplianceGuard,
96
+ ISO_42001_CLAUSES,
97
+ };
@@ -0,0 +1,244 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ThumbGate Agent Hop-Latency Budget & Fast-Path Engine
5
+ *
6
+ * Grounded in 2026 Enterprise Latency Benchmarks (The New Stack / Akamai / LangChain):
7
+ * - 82% of enterprises require <=500ms response times; 64% require <=250ms.
8
+ * - CPU/WAN multi-hop processing accounts for up to 90.6% of agent chain latency.
9
+ * - Tracks per-hop execution budgets, isolates CPU-side bottlenecks, and enforces fast-path local execution.
10
+ */
11
+
12
+ const PROFILES = {
13
+ INTERACTIVE_VOICE: {
14
+ name: 'interactive_voice',
15
+ slaMs: 250,
16
+ budgets: {
17
+ reasoning_inference: 100,
18
+ governance_gate: 15,
19
+ tool_dispatch_transport: 75,
20
+ cpu_data_processing: 35,
21
+ memory_recall: 25,
22
+ },
23
+ },
24
+ STANDARD_AGENT: {
25
+ name: 'standard_agent',
26
+ slaMs: 500,
27
+ budgets: {
28
+ reasoning_inference: 200,
29
+ governance_gate: 25,
30
+ tool_dispatch_transport: 150,
31
+ cpu_data_processing: 75,
32
+ memory_recall: 50,
33
+ },
34
+ },
35
+ BATCH_WORKFLOW: {
36
+ name: 'batch_workflow',
37
+ slaMs: 5000,
38
+ budgets: {
39
+ reasoning_inference: 2500,
40
+ governance_gate: 200,
41
+ tool_dispatch_transport: 1300,
42
+ cpu_data_processing: 600,
43
+ memory_recall: 400,
44
+ },
45
+ },
46
+ };
47
+
48
+ class LatencyTracker {
49
+ /**
50
+ * @param {'standard_agent'|'interactive_voice'|'batch_workflow'|Object} [profile='standard_agent']
51
+ */
52
+ constructor(profile = 'standard_agent') {
53
+ if (typeof profile === 'string') {
54
+ this.profile = PROFILES[profile.toUpperCase()] || PROFILES.STANDARD_AGENT;
55
+ } else if (typeof profile === 'object' && profile.slaMs) {
56
+ this.profile = profile;
57
+ } else {
58
+ this.profile = PROFILES.STANDARD_AGENT;
59
+ }
60
+
61
+ this.hops = [];
62
+ this.activeHops = new Map();
63
+ this.sessionStartTime = Date.now();
64
+ }
65
+
66
+ /**
67
+ * Starts timing a specific hop.
68
+ * @param {string} phase e.g. 'reasoning_inference', 'governance_gate', 'tool_dispatch_transport'
69
+ * @param {string} [label] descriptive label
70
+ * @returns {string} hopId
71
+ */
72
+ startHop(phase, label = '') {
73
+ const hopId = `hop_${this.hops.length + 1}_${Date.now()}`;
74
+ const startMs = Date.now();
75
+ this.activeHops.set(hopId, {
76
+ hopId,
77
+ phase,
78
+ label: label || phase,
79
+ startMs,
80
+ endMs: null,
81
+ durationMs: null,
82
+ });
83
+ return hopId;
84
+ }
85
+
86
+ /**
87
+ * Ends timing a specific hop.
88
+ * @param {string} hopId
89
+ * @param {Object} [metadata]
90
+ * @returns {Object} recorded hop
91
+ */
92
+ endHop(hopId, metadata = {}) {
93
+ const active = this.activeHops.get(hopId);
94
+ if (!active) {
95
+ throw new Error(`Unknown active hop ID: ${hopId}`);
96
+ }
97
+
98
+ const endMs = Date.now();
99
+ const durationMs = Math.max(0, endMs - active.startMs);
100
+ const completed = {
101
+ ...active,
102
+ endMs,
103
+ durationMs,
104
+ metadata,
105
+ };
106
+
107
+ this.activeHops.delete(hopId);
108
+ this.hops.push(completed);
109
+ return completed;
110
+ }
111
+
112
+ /**
113
+ * Directly records a completed hop duration.
114
+ * @param {string} phase
115
+ * @param {number} durationMs
116
+ * @param {string} [label]
117
+ * @param {Object} [metadata]
118
+ */
119
+ recordHop(phase, durationMs, label = '', metadata = {}) {
120
+ const now = Date.now();
121
+ const hop = {
122
+ hopId: `hop_${this.hops.length + 1}_${now}`,
123
+ phase,
124
+ label: label || phase,
125
+ startMs: now - durationMs,
126
+ endMs: now,
127
+ durationMs: Math.max(0, durationMs),
128
+ metadata,
129
+ };
130
+ this.hops.push(hop);
131
+ return hop;
132
+ }
133
+
134
+ /**
135
+ * Analyzes all recorded hops against performance SLA and budget limits.
136
+ * @returns {Object} analysis summary
137
+ */
138
+ analyze() {
139
+ let totalDurationMs = 0;
140
+ let cpuDurationMs = 0;
141
+ let gpuDurationMs = 0;
142
+ const phaseTotals = {};
143
+ const breachedPhases = [];
144
+
145
+ for (const hop of this.hops) {
146
+ const dur = hop.durationMs;
147
+ totalDurationMs += dur;
148
+ phaseTotals[hop.phase] = (phaseTotals[hop.phase] || 0) + dur;
149
+
150
+ if (hop.phase === 'reasoning_inference') {
151
+ gpuDurationMs += dur;
152
+ } else {
153
+ cpuDurationMs += dur;
154
+ }
155
+ }
156
+
157
+ const budgets = this.profile.budgets || {};
158
+ for (const [phase, budgetMs] of Object.entries(budgets)) {
159
+ const actual = phaseTotals[phase] || 0;
160
+ if (actual > budgetMs) {
161
+ breachedPhases.push({
162
+ phase,
163
+ budgetMs,
164
+ actualMs: actual,
165
+ excessMs: actual - budgetMs,
166
+ });
167
+ }
168
+ }
169
+
170
+ const cpuRatio = totalDurationMs > 0 ? Number((cpuDurationMs / totalDurationMs).toFixed(3)) : 0;
171
+ const meetsSla = totalDurationMs <= this.profile.slaMs;
172
+ const cpuBottleneck = cpuRatio >= 0.7; // >70% latency spent outside model reasoning
173
+
174
+ return {
175
+ profile: this.profile.name,
176
+ slaMs: this.profile.slaMs,
177
+ totalDurationMs,
178
+ meetsSla,
179
+ cpuDurationMs,
180
+ gpuDurationMs,
181
+ cpuRatio,
182
+ cpuBottleneck,
183
+ hopCount: this.hops.length,
184
+ phaseTotals,
185
+ breachedPhases,
186
+ recommendations: this._generateRecommendations(meetsSla, cpuBottleneck, breachedPhases),
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Generates actionable remediation recommendations.
192
+ * @private
193
+ */
194
+ _generateRecommendations(meetsSla, cpuBottleneck, breachedPhases) {
195
+ const recs = [];
196
+ if (!meetsSla) {
197
+ recs.push(`Target SLA missed by ${this.analyzeTotalExcess()}ms. Activate local fast-path edge caching.`);
198
+ }
199
+ if (cpuBottleneck) {
200
+ recs.push('High CPU/transport ratio (>70%). Co-locate MCP tools with local ThumbGate gateway to eliminate WAN roundtrips.');
201
+ }
202
+ for (const breach of breachedPhases) {
203
+ if (breach.phase === 'governance_gate') {
204
+ recs.push(`Governance phase exceeded budget (${breach.actualMs}ms vs ${breach.budgetMs}ms). Switch to asynchronous scrubbed audit trail.`);
205
+ } else if (breach.phase === 'tool_dispatch_transport') {
206
+ recs.push(`Tool transport exceeded budget (${breach.actualMs}ms vs ${breach.budgetMs}ms). Batch parallel tool executions.`);
207
+ }
208
+ }
209
+ return recs;
210
+ }
211
+
212
+ /**
213
+ * Calculates total excess ms over SLA.
214
+ * @private
215
+ */
216
+ analyzeTotalExcess() {
217
+ const total = this.hops.reduce((acc, h) => acc + h.durationMs, 0);
218
+ return Math.max(0, total - this.profile.slaMs);
219
+ }
220
+
221
+ /**
222
+ * Exports OpenTelemetry-compliant trace span attributes.
223
+ * @returns {Object}
224
+ */
225
+ exportOtelAttributes() {
226
+ const report = this.analyze();
227
+ return {
228
+ 'agent.latency.profile': report.profile,
229
+ 'agent.latency.sla_ms': report.slaMs,
230
+ 'agent.latency.total_ms': report.totalDurationMs,
231
+ 'agent.latency.cpu_ms': report.cpuDurationMs,
232
+ 'agent.latency.gpu_ms': report.gpuDurationMs,
233
+ 'agent.latency.cpu_ratio': report.cpuRatio,
234
+ 'agent.latency.cpu_bottleneck': report.cpuBottleneck,
235
+ 'agent.latency.meets_sla': report.meetsSla,
236
+ 'agent.latency.hop_count': report.hopCount,
237
+ };
238
+ }
239
+ }
240
+
241
+ module.exports = {
242
+ PROFILES,
243
+ LatencyTracker,
244
+ };
@@ -0,0 +1,316 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ThumbGate WriteGuard — Fine-Grained Pre-Action MCP Governance Engine
5
+ *
6
+ * Inspired by Cloudflare WriteGuard & ThumbGate Reliability Gateway:
7
+ * - Intercepts incoming MCP tool calls without upstream server modification
8
+ * - Classifies tool risk tiers (read, write, privileged_write, admin)
9
+ * - Evaluates request context against deterministic safety policies
10
+ * - Scrubs sensitive tokens/secrets from audit payloads
11
+ * - Emits structured attribution receipts with user, client, session, and duration metadata
12
+ */
13
+
14
+ const crypto = require('node:crypto');
15
+
16
+ const RISK_TIERS = {
17
+ READ: 'read',
18
+ WRITE: 'write',
19
+ PRIVILEGED_WRITE: 'privileged_write',
20
+ ADMIN: 'admin',
21
+ };
22
+
23
+ const DEFAULT_READ_TOOLS = new Set([
24
+ 'view_file',
25
+ 'read_file',
26
+ 'list_dir',
27
+ 'find_by_name',
28
+ 'grep_search',
29
+ 'read_resource',
30
+ 'list_resources',
31
+ 'search_web',
32
+ 'read_url_content',
33
+ 'get_scope_state',
34
+ 'get_branch_governance',
35
+ 'get_action_receipts',
36
+ 'get_task_outcomes',
37
+ 'get_business_metrics',
38
+ 'estimate_uncertainty',
39
+ 'recall',
40
+ 'feedback_summary',
41
+ 'search_lessons',
42
+ 'retrieve_lessons',
43
+ 'dashboard',
44
+ ]);
45
+
46
+ const DEFAULT_WRITE_TOOLS = new Set([
47
+ 'write_to_file',
48
+ 'replace_file_content',
49
+ 'edit_file',
50
+ 'generate_image',
51
+ 'capture_feedback',
52
+ 'capture_memory_feedback',
53
+ 'record_action_receipt',
54
+ 'record_task_outcome',
55
+ 'track_action',
56
+ ]);
57
+
58
+ const DEFAULT_PRIVILEGED_TOOLS = new Set([
59
+ 'run_command',
60
+ 'execute_command',
61
+ 'bash',
62
+ 'manage_task',
63
+ 'kill',
64
+ 'create_purchase_requisition',
65
+ 'settle_purchase_requisition',
66
+ 'browser_run_code_unsafe',
67
+ 'browser_evaluate',
68
+ ]);
69
+
70
+ const DEFAULT_ADMIN_TOOLS = new Set([
71
+ 'set_branch_governance',
72
+ 'approve_protected_action',
73
+ 'register_claim_gate',
74
+ 'define_subagent',
75
+ 'manage_subagents',
76
+ 'satisfy_gate',
77
+ ]);
78
+
79
+ const DANGEROUS_COMMAND_PATTERNS = [
80
+ /rm\s+-rf\s+[/~]/i,
81
+ /drop\s+table/i,
82
+ /drop\s+database/i,
83
+ /truncate\s+table/i,
84
+ /git\s+push\s+[\s\S]*?--force/i,
85
+ /git\s+reset\s+--hard/i,
86
+ /chmod\s+777/i,
87
+ /mkfs/i,
88
+ />\s*\/dev\/sd[a-z]/i,
89
+ /:()\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;/i, // fork bomb
90
+ ];
91
+
92
+ const SENSITIVE_KEY_PATTERNS = [
93
+ /api[_-]?key/i,
94
+ /auth[_-]?token/i,
95
+ /access[_-]?token/i,
96
+ /bearer/i,
97
+ /password/i,
98
+ /passwd/i,
99
+ /secret/i,
100
+ /private[_-]?key/i,
101
+ /credentials/i,
102
+ /stripe[_-]?key/i,
103
+ /github[_-]?pat/i,
104
+ /^(authorization|proxy[_-]?authorization|cookie|set[_-]?cookie)$/i,
105
+ /x[_-]?relay/i,
106
+ ];
107
+
108
+ const CREDENTIAL_HEADER_KEYS = /^(authorization|proxy-authorization|cookie|set-cookie)$/i;
109
+
110
+ /**
111
+ * Classifies an MCP tool into a risk tier.
112
+ * @param {string} toolName
113
+ * @param {Object} [customPolicies]
114
+ * @returns {'read'|'write'|'privileged_write'|'admin'}
115
+ */
116
+ function classifyMcpTool(toolName, customPolicies = {}) {
117
+ if (!toolName || typeof toolName !== 'string') return RISK_TIERS.READ;
118
+ const name = toolName.trim();
119
+
120
+ if (customPolicies[name] && customPolicies[name].tier) {
121
+ return customPolicies[name].tier;
122
+ }
123
+
124
+ if (DEFAULT_ADMIN_TOOLS.has(name)) return RISK_TIERS.ADMIN;
125
+ if (DEFAULT_PRIVILEGED_TOOLS.has(name)) return RISK_TIERS.PRIVILEGED_WRITE;
126
+ if (DEFAULT_WRITE_TOOLS.has(name)) return RISK_TIERS.WRITE;
127
+ if (DEFAULT_READ_TOOLS.has(name)) return RISK_TIERS.READ;
128
+
129
+ // Heuristic fallbacks for custom/MCP servers
130
+ if (/^(delete|drop|destroy|purge|remove|kill|admin|grant|revoke)/i.test(name)) {
131
+ return RISK_TIERS.PRIVILEGED_WRITE;
132
+ }
133
+ if (/^(create|write|update|set|put|post|patch|insert|modify|send)/i.test(name)) {
134
+ return RISK_TIERS.WRITE;
135
+ }
136
+
137
+ return RISK_TIERS.READ;
138
+ }
139
+
140
+ /**
141
+ * Scrub credential-bearing string values (headers and inline tokens).
142
+ * @param {string} value
143
+ * @returns {string}
144
+ */
145
+ function scrubSensitiveString(value) {
146
+ let out = value;
147
+ if (/-----BEGIN\s+(RSA|OPENSSH|PRIVATE)\s+KEY-----/i.test(out)) {
148
+ return '[REDACTED_PRIVATE_KEY]';
149
+ }
150
+ if (/\bbearer\s+[a-zA-Z0-9_\-.]{20,}/i.test(out)) {
151
+ out = out.replace(/\bbearer\s+[a-zA-Z0-9_\-.]{20,}/gi, 'Bearer [REDACTED]');
152
+ }
153
+ if (/\bbasic\s+[a-zA-Z0-9+/=_-]{8,}/i.test(out)) {
154
+ out = out.replace(/\bbasic\s+[a-zA-Z0-9+/=_-]{8,}/gi, 'Basic [REDACTED]');
155
+ }
156
+ // Cookie / Set-Cookie style values (name=value pairs with auth-ish names)
157
+ if (
158
+ /(?:^|;\s*)(?:session|sid|token|auth|jwt|refresh)[^=]*=[^;]+/i.test(out)
159
+ || (/^[^=;\s]+=[^;]+(?:;\s*[^=;\s]+=[^;]+)*$/.test(out.trim())
160
+ && /(session|token|sid|auth|jwt|secret)/i.test(out))
161
+ ) {
162
+ return '[REDACTED_COOKIE]';
163
+ }
164
+ return out;
165
+ }
166
+
167
+ /**
168
+ * Recursively scrubs secret keys and values from parameter objects.
169
+ * @param {*} value
170
+ * @returns {*}
171
+ */
172
+ function scrubSensitiveData(value) {
173
+ if (value === null || value === undefined) return value;
174
+ if (typeof value === 'string') {
175
+ return scrubSensitiveString(value);
176
+ }
177
+
178
+ if (Array.isArray(value)) {
179
+ return value.map(scrubSensitiveData);
180
+ }
181
+
182
+ if (typeof value === 'object') {
183
+ const scrubbed = {};
184
+ for (const [k, v] of Object.entries(value)) {
185
+ const isSensitiveKey = SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(k))
186
+ || CREDENTIAL_HEADER_KEYS.test(String(k).trim());
187
+ if (isSensitiveKey) {
188
+ scrubbed[k] = '[REDACTED]';
189
+ } else {
190
+ scrubbed[k] = scrubSensitiveData(v);
191
+ }
192
+ }
193
+ return scrubbed;
194
+ }
195
+
196
+ return value;
197
+ }
198
+
199
+ /**
200
+ * Inspects parameters for dangerous destructive operations.
201
+ * @param {string} toolName
202
+ * @param {Object} parameters
203
+ * @returns {Array<string>} list of violation reasons
204
+ */
205
+ function scanForDestructivePatterns(toolName, parameters) {
206
+ const violations = [];
207
+ if (!parameters || typeof parameters !== 'object') return violations;
208
+
209
+ const serialized = JSON.stringify(parameters);
210
+ for (const pat of DANGEROUS_COMMAND_PATTERNS) {
211
+ if (pat.test(serialized)) {
212
+ violations.push(`Dangerous destructive pattern detected matching ${pat.toString()}`);
213
+ }
214
+ }
215
+
216
+ return violations;
217
+ }
218
+
219
+ /**
220
+ * Evaluates an incoming MCP call and produces an attribution receipt.
221
+ * @param {Object} callRequest
222
+ * @param {string} callRequest.server
223
+ * @param {string} callRequest.tool
224
+ * @param {Object} callRequest.parameters
225
+ * @param {Object} [callRequest.context]
226
+ * @param {Object} [options]
227
+ * @returns {Object} evaluation receipt
228
+ */
229
+ function evaluateMcpCall(callRequest, options = {}) {
230
+ const startTime = Date.now();
231
+ const server = callRequest.server || 'default';
232
+ const tool = callRequest.tool || 'unknown';
233
+ const parameters = callRequest.parameters || {};
234
+ const context = callRequest.context || {};
235
+ const customPolicies = options.customPolicies || {};
236
+
237
+ const riskTier = classifyMcpTool(tool, customPolicies);
238
+ // Destructive command patterns apply to executable/write tiers only.
239
+ // Read tools (retrieve_lessons, recall, search) legitimately quote
240
+ // force-push / rm -rf text as lesson queries — scanning those is a false deny.
241
+ const violations = riskTier === RISK_TIERS.READ
242
+ ? []
243
+ : scanForDestructivePatterns(tool, parameters);
244
+
245
+ let decision = 'allowed';
246
+ const reasons = [...violations];
247
+
248
+ // Evaluate tier restrictions
249
+ if (violations.length > 0) {
250
+ decision = 'blocked';
251
+ } else if (riskTier === RISK_TIERS.ADMIN && !options.allowAdmin) {
252
+ decision = 'escalated';
253
+ reasons.push(`Tool ${tool} is in admin tier and requires explicit operator approval`);
254
+ } else if (riskTier === RISK_TIERS.PRIVILEGED_WRITE && options.requireReviewForPrivileged) {
255
+ decision = 'escalated';
256
+ reasons.push(`Tool ${tool} is in privileged_write tier and requires human review`);
257
+ }
258
+
259
+ const durationMs = Math.max(0, Date.now() - startTime);
260
+ const eventId = `wg_${Date.now()}_${crypto.randomBytes(4).toString('hex')}`;
261
+
262
+ const receipt = {
263
+ eventId,
264
+ timestamp: new Date().toISOString(),
265
+ server,
266
+ tool,
267
+ riskTier,
268
+ decision,
269
+ user: context.user || 'operator',
270
+ client: context.client || 'mcp-client',
271
+ sessionId: context.sessionId || 'default-session',
272
+ durationMs,
273
+ reasons,
274
+ parameters: scrubSensitiveData(parameters),
275
+ };
276
+
277
+ return receipt;
278
+ }
279
+
280
+ /**
281
+ * Exports a Cloudflare WriteGuard compatible JSON policy schema.
282
+ * @param {Array<string>} toolNames
283
+ * @param {Object} [overrides]
284
+ * @returns {Object}
285
+ */
286
+ function exportCloudflareWriteGuardPolicy(toolNames = [], overrides = {}) {
287
+ const policies = {};
288
+
289
+ for (const name of toolNames) {
290
+ const tier = classifyMcpTool(name, overrides);
291
+ policies[name] = {
292
+ riskTier: tier,
293
+ action: tier === RISK_TIERS.ADMIN ? 'escalate' : tier === RISK_TIERS.PRIVILEGED_WRITE ? 'guard' : 'allow',
294
+ audit: true,
295
+ redactSecrets: true,
296
+ maxExecutionMs: tier === RISK_TIERS.READ ? 2000 : 10000,
297
+ };
298
+ }
299
+
300
+ return {
301
+ version: '1.0',
302
+ generatedAt: new Date().toISOString(),
303
+ engine: 'ThumbGate-WriteGuard',
304
+ defaultAction: 'allow',
305
+ policies,
306
+ };
307
+ }
308
+
309
+ module.exports = {
310
+ RISK_TIERS,
311
+ classifyMcpTool,
312
+ scrubSensitiveData,
313
+ scanForDestructivePatterns,
314
+ evaluateMcpCall,
315
+ exportCloudflareWriteGuardPolicy,
316
+ };